mirror of
https://github.com/apotdevin/thunderhub.git
synced 2024-11-19 18:00:05 +01:00
feat: keysend integration (#27)
* feat: add keysend payments * refactor: change file name * refactor: set correct name * chore: add keysend experimental warning
This commit is contained in:
parent
f29ba3d2ae
commit
b4473c50bd
@ -13,7 +13,7 @@ import { useAccount } from '../src/context/AccountContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../src/utils/error';
|
||||
import { LoadingCard } from '../src/components/loading/LoadingCard';
|
||||
import { getPercent } from '../src/utils/Helpers';
|
||||
import { getPercent } from '../src/utils/helpers';
|
||||
import { Input } from '../src/components/input/Input';
|
||||
import sortBy from 'lodash.sortby';
|
||||
import { BalanceCard } from '../src/views/balance/BalanceCard';
|
||||
|
@ -13,7 +13,7 @@ import { toast } from 'react-toastify';
|
||||
import { Link } from '../src/components/link/Link';
|
||||
import { ColorButton } from '../src/components/buttons/colorButton/ColorButton';
|
||||
import { useRouter } from 'next/router';
|
||||
import { decode } from '../src/utils/Helpers';
|
||||
import { decode } from '../src/utils/helpers';
|
||||
import { useGetOffersQuery } from '../src/generated/graphql';
|
||||
|
||||
export interface QueryProps {
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { parsePayment } from './parsePayment';
|
||||
import { pay } from './pay';
|
||||
import { createInvoice } from './createInvoice';
|
||||
import { decodeRequest } from './decode';
|
||||
import { payViaRoute } from './payViaRoute';
|
||||
|
||||
export const invoices = {
|
||||
parsePayment,
|
||||
pay,
|
||||
createInvoice,
|
||||
decodeRequest,
|
||||
payViaRoute,
|
||||
};
|
||||
|
@ -1,10 +1,17 @@
|
||||
import { pay as payRequest } from 'ln-service';
|
||||
import {
|
||||
pay as payRequest,
|
||||
decodePaymentRequest,
|
||||
payViaPaymentDetails,
|
||||
} from 'ln-service';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { GraphQLString, GraphQLNonNull, GraphQLInt } from 'graphql';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { PayType } from '../../types/MutationType';
|
||||
import { randomBytes, createHash } from 'crypto';
|
||||
|
||||
const KEYSEND_TYPE = '5482373484';
|
||||
|
||||
interface HopProps {
|
||||
channel: string;
|
||||
@ -23,6 +30,20 @@ interface RequestProps {
|
||||
is_outgoing: boolean;
|
||||
mtokens: string;
|
||||
secret: string;
|
||||
safe_fee: boolean;
|
||||
safe_tokens: boolean;
|
||||
tokens: number;
|
||||
}
|
||||
|
||||
interface DetailsProps {
|
||||
fee: number;
|
||||
fee_mtokens: string;
|
||||
hops: HopProps[];
|
||||
id: string;
|
||||
mtokens: string;
|
||||
safe_fee: boolean;
|
||||
safe_tokens: boolean;
|
||||
secret: string;
|
||||
tokens: number;
|
||||
}
|
||||
|
||||
@ -32,39 +53,58 @@ export const pay = {
|
||||
args: {
|
||||
...defaultParams,
|
||||
request: { type: new GraphQLNonNull(GraphQLString) },
|
||||
tokens: { type: GraphQLInt },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
await requestLimiter(context.ip, 'pay');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
|
||||
let isRequest = false;
|
||||
try {
|
||||
const payment: RequestProps = await payRequest({
|
||||
await decodePaymentRequest({
|
||||
lnd,
|
||||
request: params.request,
|
||||
});
|
||||
isRequest = true;
|
||||
} catch (error) {
|
||||
params.logger && logger.error('Error decoding request: %o', error);
|
||||
}
|
||||
|
||||
const hops = payment.hops.map(hop => {
|
||||
return {
|
||||
channel: hop.channel,
|
||||
channelCapacity: hop.channel_capacity,
|
||||
mTokenFee: hop.fee_mtokens,
|
||||
forwardMTokens: hop.forward_mtokens,
|
||||
timeout: hop.timeout,
|
||||
};
|
||||
if (isRequest) {
|
||||
try {
|
||||
const payment: RequestProps = await payRequest({
|
||||
lnd,
|
||||
request: params.request,
|
||||
});
|
||||
return payment;
|
||||
} catch (error) {
|
||||
params.logger && logger.error('Error paying request: %o', error);
|
||||
throw new Error(getErrorMsg(error));
|
||||
}
|
||||
}
|
||||
|
||||
if (!params.tokens) {
|
||||
throw new Error('Amount of tokens is needed for keysend');
|
||||
}
|
||||
|
||||
const preimage = randomBytes(32);
|
||||
const secret = preimage.toString('hex');
|
||||
const id = createHash('sha256').update(preimage).digest().toString('hex');
|
||||
|
||||
try {
|
||||
const payment: DetailsProps = await payViaPaymentDetails({
|
||||
id,
|
||||
lnd,
|
||||
tokens: params.tokens,
|
||||
destination: params.request,
|
||||
messages: [
|
||||
{
|
||||
type: KEYSEND_TYPE,
|
||||
value: secret,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
fee: payment.fee,
|
||||
feeMTokens: payment.fee_mtokens,
|
||||
hops,
|
||||
id: payment.id,
|
||||
isConfirmed: payment.is_confirmed,
|
||||
isOutgoing: payment.is_outgoing,
|
||||
mtokens: payment.mtokens,
|
||||
secret: payment.secret,
|
||||
tokens: payment.tokens,
|
||||
};
|
||||
return payment;
|
||||
} catch (error) {
|
||||
params.logger && logger.error('Error paying request: %o', error);
|
||||
throw new Error(getErrorMsg(error));
|
||||
|
@ -4,7 +4,7 @@ import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { DecodeType } from '../../types/MutationType';
|
||||
import { DecodeType } from '../../types/QueryType';
|
||||
|
||||
interface RouteProps {
|
||||
base_fee_mtokens: string;
|
||||
@ -43,47 +43,7 @@ export const decodeRequest = {
|
||||
request: params.request,
|
||||
});
|
||||
|
||||
const routes = decode.routes.map(route => {
|
||||
route.map(nodeChannel => {
|
||||
const {
|
||||
base_fee_mtokens,
|
||||
channel,
|
||||
cltv_delta,
|
||||
fee_rate,
|
||||
public_key,
|
||||
} = nodeChannel;
|
||||
return {
|
||||
baseFeeTokens: base_fee_mtokens,
|
||||
channel,
|
||||
cltvDelta: cltv_delta,
|
||||
feeRate: fee_rate,
|
||||
publicKey: public_key,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const {
|
||||
chain_address,
|
||||
cltv_delta,
|
||||
description,
|
||||
description_hash,
|
||||
destination,
|
||||
expires_at,
|
||||
id,
|
||||
tokens,
|
||||
} = decode;
|
||||
|
||||
return {
|
||||
chainAddress: chain_address,
|
||||
cltvDelta: cltv_delta,
|
||||
description,
|
||||
descriptionHash: description_hash,
|
||||
destination,
|
||||
expiresAt: expires_at,
|
||||
id,
|
||||
routes,
|
||||
tokens,
|
||||
};
|
||||
return decode;
|
||||
} catch (error) {
|
||||
params.logger && logger.error('Error paying request: %o', error);
|
||||
throw new Error(getErrorMsg(error));
|
34
src/api/schemas/query/general/getNode.ts
Normal file
34
src/api/schemas/query/general/getNode.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { GraphQLString, GraphQLNonNull, GraphQLBoolean } from 'graphql';
|
||||
import { getNode as getLnNode } from 'ln-service';
|
||||
import { logger } from '../../../helpers/logger';
|
||||
import { requestLimiter } from '../../../helpers/rateLimiter';
|
||||
import { getErrorMsg, getAuthLnd } from '../../../helpers/helpers';
|
||||
|
||||
import { defaultParams } from '../../../helpers/defaultProps';
|
||||
import { PartnerNodeType } from '../../types/QueryType';
|
||||
|
||||
export const getNode = {
|
||||
type: PartnerNodeType,
|
||||
args: {
|
||||
...defaultParams,
|
||||
publicKey: { type: new GraphQLNonNull(GraphQLString) },
|
||||
withoutChannels: { type: GraphQLBoolean },
|
||||
},
|
||||
resolve: async (root: any, params: any, context: any) => {
|
||||
await requestLimiter(context.ip, 'closedChannels');
|
||||
|
||||
const lnd = getAuthLnd(params.auth);
|
||||
|
||||
try {
|
||||
const nodeInfo = await getLnNode({
|
||||
lnd,
|
||||
is_omitting_channels: params.withoutChannels ?? true,
|
||||
public_key: params.publicKey,
|
||||
});
|
||||
return nodeInfo;
|
||||
} catch (error) {
|
||||
params.logger && logger.error('Error getting closed channels: %o', error);
|
||||
throw new Error(getErrorMsg(error));
|
||||
}
|
||||
},
|
||||
};
|
@ -1,9 +1,13 @@
|
||||
import { getNetworkInfo } from './networkInfo';
|
||||
import { getNodeInfo } from './nodeInfo';
|
||||
import { getNode } from './getNode';
|
||||
import { adminCheck } from './adminCheck';
|
||||
import { decodeRequest } from './decode';
|
||||
|
||||
export const generalQueries = {
|
||||
getNetworkInfo,
|
||||
getNodeInfo,
|
||||
adminCheck,
|
||||
getNode,
|
||||
decodeRequest,
|
||||
};
|
||||
|
@ -42,34 +42,6 @@ export const InvoiceType = new GraphQLObjectType({
|
||||
},
|
||||
});
|
||||
|
||||
const DecodeRoutesType = new GraphQLObjectType({
|
||||
name: 'DecodeRoutesType',
|
||||
fields: () => ({
|
||||
baseFeeMTokens: { type: GraphQLString },
|
||||
channel: { type: GraphQLString },
|
||||
cltvDelta: { type: GraphQLInt },
|
||||
feeRate: { type: GraphQLInt },
|
||||
publicKey: { type: GraphQLString },
|
||||
}),
|
||||
});
|
||||
|
||||
export const DecodeType = new GraphQLObjectType({
|
||||
name: 'decodeType',
|
||||
fields: () => {
|
||||
return {
|
||||
chainAddress: { type: GraphQLString },
|
||||
cltvDelta: { type: GraphQLInt },
|
||||
description: { type: GraphQLString },
|
||||
descriptionHash: { type: GraphQLString },
|
||||
destination: { type: GraphQLString },
|
||||
expiresAt: { type: GraphQLString },
|
||||
id: { type: GraphQLString },
|
||||
routes: { type: new GraphQLList(DecodeRoutesType) },
|
||||
tokens: { type: GraphQLInt },
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const PaymentRouteType = new GraphQLObjectType({
|
||||
name: 'PaymentRouteType',
|
||||
fields: () => ({
|
||||
@ -106,9 +78,9 @@ const HopsType = new GraphQLObjectType({
|
||||
name: 'hopsType',
|
||||
fields: () => ({
|
||||
channel: { type: GraphQLString },
|
||||
channelCapacity: { type: GraphQLInt },
|
||||
mTokenFee: { type: GraphQLString },
|
||||
forwardMTokens: { type: GraphQLString },
|
||||
channel_capacity: { type: GraphQLInt },
|
||||
fee_mtokens: { type: GraphQLString },
|
||||
forward_mtokens: { type: GraphQLString },
|
||||
timeout: { type: GraphQLInt },
|
||||
}),
|
||||
});
|
||||
@ -118,13 +90,15 @@ export const PayType = new GraphQLObjectType({
|
||||
fields: () => {
|
||||
return {
|
||||
fee: { type: GraphQLInt },
|
||||
feeMTokens: { type: GraphQLString },
|
||||
fee_mtokens: { type: GraphQLString },
|
||||
hops: { type: new GraphQLList(HopsType) },
|
||||
id: { type: GraphQLString },
|
||||
isConfirmed: { type: GraphQLBoolean },
|
||||
isOutgoing: { type: GraphQLBoolean },
|
||||
is_confirmed: { type: GraphQLBoolean },
|
||||
is_outgoing: { type: GraphQLBoolean },
|
||||
mtokens: { type: GraphQLString },
|
||||
secret: { type: GraphQLString },
|
||||
safe_fee: { type: GraphQLInt },
|
||||
safe_tokens: { type: GraphQLInt },
|
||||
tokens: { type: GraphQLInt },
|
||||
};
|
||||
},
|
||||
|
@ -42,6 +42,36 @@ export const ChannelReportType = new GraphQLObjectType({
|
||||
},
|
||||
});
|
||||
|
||||
const DecodeRoutesType = new GraphQLObjectType({
|
||||
name: 'DecodeRoutesType',
|
||||
fields: () => ({
|
||||
base_fee_mtokens: { type: GraphQLString },
|
||||
channel: { type: GraphQLString },
|
||||
cltv_delta: { type: GraphQLInt },
|
||||
fee_rate: { type: GraphQLInt },
|
||||
public_key: { type: GraphQLString },
|
||||
}),
|
||||
});
|
||||
|
||||
export const DecodeType = new GraphQLObjectType({
|
||||
name: 'decodeType',
|
||||
fields: () => {
|
||||
return {
|
||||
chain_address: { type: GraphQLString },
|
||||
cltv_delta: { type: GraphQLInt },
|
||||
description: { type: GraphQLString },
|
||||
description_hash: { type: GraphQLString },
|
||||
destination: { type: GraphQLString },
|
||||
expires_at: { type: GraphQLString },
|
||||
id: { type: GraphQLString },
|
||||
mtokens: { type: GraphQLString },
|
||||
routes: { type: new GraphQLList(DecodeRoutesType) },
|
||||
safe_tokens: { type: GraphQLInt },
|
||||
tokens: { type: GraphQLInt },
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const PartnerNodeType = new GraphQLObjectType({
|
||||
name: 'partnerNodeType',
|
||||
fields: () => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useSpring, animated } from 'react-spring';
|
||||
import { getValue } from '../../utils/Helpers';
|
||||
import { getValue } from '../../utils/helpers';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
|
||||
|
@ -170,13 +170,13 @@ export const SimpleInverseButton = styled(SimpleButton)`
|
||||
|
||||
interface DarkProps {
|
||||
fontSize?: string;
|
||||
bottom?: string;
|
||||
withMargin?: string;
|
||||
}
|
||||
|
||||
export const DarkSubTitle = styled.div`
|
||||
font-size: ${({ fontSize }: DarkProps) => (fontSize ? fontSize : '14px')};
|
||||
color: ${unSelectedNavButton};
|
||||
margin-bottom: ${({ bottom }: DarkProps) => (bottom ? bottom : '0px')};
|
||||
margin: ${({ withMargin }: DarkProps) => (withMargin ? withMargin : '0')};
|
||||
`;
|
||||
|
||||
interface ColorProps {
|
||||
|
@ -12,7 +12,7 @@ import { QuestionIcon } from '../generic/Icons';
|
||||
import styled from 'styled-components';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { getTooltipType } from '../generic/Helpers';
|
||||
import { getTooltipType } from '../generic/helpers';
|
||||
|
||||
const StyledQuestion = styled(QuestionIcon)`
|
||||
margin-left: 8px;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import { getValue } from '../../utils/Helpers';
|
||||
import { getValue } from '../../utils/helpers';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
|
||||
type PriceProps = {
|
||||
|
@ -24,6 +24,8 @@ export type Query = {
|
||||
getNetworkInfo?: Maybe<NetworkInfoType>;
|
||||
getNodeInfo?: Maybe<NodeInfoType>;
|
||||
adminCheck?: Maybe<Scalars['Boolean']>;
|
||||
getNode?: Maybe<PartnerNodeType>;
|
||||
decodeRequest?: Maybe<DecodeType>;
|
||||
getResume?: Maybe<GetResumeType>;
|
||||
getForwards?: Maybe<GetForwardType>;
|
||||
getBitcoinPrice?: Maybe<Scalars['String']>;
|
||||
@ -94,6 +96,19 @@ export type QueryAdminCheckArgs = {
|
||||
logger?: Maybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type QueryGetNodeArgs = {
|
||||
auth: AuthType;
|
||||
logger?: Maybe<Scalars['Boolean']>;
|
||||
publicKey: Scalars['String'];
|
||||
withoutChannels?: Maybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type QueryDecodeRequestArgs = {
|
||||
auth: AuthType;
|
||||
logger?: Maybe<Scalars['Boolean']>;
|
||||
request: Scalars['String'];
|
||||
};
|
||||
|
||||
export type QueryGetResumeArgs = {
|
||||
auth: AuthType;
|
||||
logger?: Maybe<Scalars['Boolean']>;
|
||||
@ -338,6 +353,30 @@ export type NodeInfoType = {
|
||||
version?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type DecodeType = {
|
||||
__typename?: 'decodeType';
|
||||
chain_address?: Maybe<Scalars['String']>;
|
||||
cltv_delta?: Maybe<Scalars['Int']>;
|
||||
description?: Maybe<Scalars['String']>;
|
||||
description_hash?: Maybe<Scalars['String']>;
|
||||
destination?: Maybe<Scalars['String']>;
|
||||
expires_at?: Maybe<Scalars['String']>;
|
||||
id?: Maybe<Scalars['String']>;
|
||||
mtokens?: Maybe<Scalars['String']>;
|
||||
routes?: Maybe<Array<Maybe<DecodeRoutesType>>>;
|
||||
safe_tokens?: Maybe<Scalars['Int']>;
|
||||
tokens?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type DecodeRoutesType = {
|
||||
__typename?: 'DecodeRoutesType';
|
||||
base_fee_mtokens?: Maybe<Scalars['String']>;
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
cltv_delta?: Maybe<Scalars['Int']>;
|
||||
fee_rate?: Maybe<Scalars['Int']>;
|
||||
public_key?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type GetResumeType = {
|
||||
__typename?: 'getResumeType';
|
||||
token?: Maybe<Scalars['String']>;
|
||||
@ -497,7 +536,6 @@ export type Mutation = {
|
||||
parsePayment?: Maybe<ParsePaymentType>;
|
||||
pay?: Maybe<PayType>;
|
||||
createInvoice?: Maybe<InvoiceType>;
|
||||
decodeRequest?: Maybe<DecodeType>;
|
||||
payViaRoute?: Maybe<Scalars['Boolean']>;
|
||||
createAddress?: Maybe<Scalars['String']>;
|
||||
sendToAddress?: Maybe<SendToType>;
|
||||
@ -542,6 +580,7 @@ export type MutationPayArgs = {
|
||||
auth: AuthType;
|
||||
logger?: Maybe<Scalars['Boolean']>;
|
||||
request: Scalars['String'];
|
||||
tokens?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type MutationCreateInvoiceArgs = {
|
||||
@ -550,12 +589,6 @@ export type MutationCreateInvoiceArgs = {
|
||||
amount: Scalars['Int'];
|
||||
};
|
||||
|
||||
export type MutationDecodeRequestArgs = {
|
||||
auth: AuthType;
|
||||
logger?: Maybe<Scalars['Boolean']>;
|
||||
request: Scalars['String'];
|
||||
};
|
||||
|
||||
export type MutationPayViaRouteArgs = {
|
||||
auth: AuthType;
|
||||
logger?: Maybe<Scalars['Boolean']>;
|
||||
@ -633,22 +666,24 @@ export type PaymentRouteType = {
|
||||
export type PayType = {
|
||||
__typename?: 'payType';
|
||||
fee?: Maybe<Scalars['Int']>;
|
||||
feeMTokens?: Maybe<Scalars['String']>;
|
||||
fee_mtokens?: Maybe<Scalars['String']>;
|
||||
hops?: Maybe<Array<Maybe<HopsType>>>;
|
||||
id?: Maybe<Scalars['String']>;
|
||||
isConfirmed?: Maybe<Scalars['Boolean']>;
|
||||
isOutgoing?: Maybe<Scalars['Boolean']>;
|
||||
is_confirmed?: Maybe<Scalars['Boolean']>;
|
||||
is_outgoing?: Maybe<Scalars['Boolean']>;
|
||||
mtokens?: Maybe<Scalars['String']>;
|
||||
secret?: Maybe<Scalars['String']>;
|
||||
safe_fee?: Maybe<Scalars['Int']>;
|
||||
safe_tokens?: Maybe<Scalars['Int']>;
|
||||
tokens?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type HopsType = {
|
||||
__typename?: 'hopsType';
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
channelCapacity?: Maybe<Scalars['Int']>;
|
||||
mTokenFee?: Maybe<Scalars['String']>;
|
||||
forwardMTokens?: Maybe<Scalars['String']>;
|
||||
channel_capacity?: Maybe<Scalars['Int']>;
|
||||
fee_mtokens?: Maybe<Scalars['String']>;
|
||||
forward_mtokens?: Maybe<Scalars['String']>;
|
||||
timeout?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
@ -663,28 +698,6 @@ export type InvoiceType = {
|
||||
tokens?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type DecodeType = {
|
||||
__typename?: 'decodeType';
|
||||
chainAddress?: Maybe<Scalars['String']>;
|
||||
cltvDelta?: Maybe<Scalars['Int']>;
|
||||
description?: Maybe<Scalars['String']>;
|
||||
descriptionHash?: Maybe<Scalars['String']>;
|
||||
destination?: Maybe<Scalars['String']>;
|
||||
expiresAt?: Maybe<Scalars['String']>;
|
||||
id?: Maybe<Scalars['String']>;
|
||||
routes?: Maybe<Array<Maybe<DecodeRoutesType>>>;
|
||||
tokens?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type DecodeRoutesType = {
|
||||
__typename?: 'DecodeRoutesType';
|
||||
baseFeeMTokens?: Maybe<Scalars['String']>;
|
||||
channel?: Maybe<Scalars['String']>;
|
||||
cltvDelta?: Maybe<Scalars['Int']>;
|
||||
feeRate?: Maybe<Scalars['Int']>;
|
||||
publicKey?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type SendToType = {
|
||||
__typename?: 'sendToType';
|
||||
confirmationCount?: Maybe<Scalars['String']>;
|
||||
@ -832,10 +845,11 @@ export type OpenChannelMutation = { __typename?: 'Mutation' } & {
|
||||
export type PayInvoiceMutationVariables = {
|
||||
request: Scalars['String'];
|
||||
auth: AuthType;
|
||||
tokens?: Maybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
export type PayInvoiceMutation = { __typename?: 'Mutation' } & {
|
||||
pay?: Maybe<{ __typename?: 'payType' } & Pick<PayType, 'isConfirmed'>>;
|
||||
pay?: Maybe<{ __typename?: 'payType' } & Pick<PayType, 'is_confirmed'>>;
|
||||
};
|
||||
|
||||
export type CreateInvoiceMutationVariables = {
|
||||
@ -877,42 +891,6 @@ export type PayAddressMutation = { __typename?: 'Mutation' } & {
|
||||
>;
|
||||
};
|
||||
|
||||
export type DecodeRequestMutationVariables = {
|
||||
auth: AuthType;
|
||||
request: Scalars['String'];
|
||||
};
|
||||
|
||||
export type DecodeRequestMutation = { __typename?: 'Mutation' } & {
|
||||
decodeRequest?: Maybe<
|
||||
{ __typename?: 'decodeType' } & Pick<
|
||||
DecodeType,
|
||||
| 'chainAddress'
|
||||
| 'cltvDelta'
|
||||
| 'description'
|
||||
| 'descriptionHash'
|
||||
| 'destination'
|
||||
| 'expiresAt'
|
||||
| 'id'
|
||||
| 'tokens'
|
||||
> & {
|
||||
routes?: Maybe<
|
||||
Array<
|
||||
Maybe<
|
||||
{ __typename?: 'DecodeRoutesType' } & Pick<
|
||||
DecodeRoutesType,
|
||||
| 'baseFeeMTokens'
|
||||
| 'channel'
|
||||
| 'cltvDelta'
|
||||
| 'feeRate'
|
||||
| 'publicKey'
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
export type UpdateFeesMutationVariables = {
|
||||
auth: AuthType;
|
||||
transactionId?: Maybe<Scalars['String']>;
|
||||
@ -1099,6 +1077,57 @@ export type GetChannelsQuery = { __typename?: 'Query' } & {
|
||||
>;
|
||||
};
|
||||
|
||||
export type GetNodeQueryVariables = {
|
||||
auth: AuthType;
|
||||
publicKey: Scalars['String'];
|
||||
withoutChannels?: Maybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type GetNodeQuery = { __typename?: 'Query' } & {
|
||||
getNode?: Maybe<
|
||||
{ __typename?: 'partnerNodeType' } & Pick<
|
||||
PartnerNodeType,
|
||||
'alias' | 'capacity' | 'channel_count' | 'color' | 'updated_at'
|
||||
>
|
||||
>;
|
||||
};
|
||||
|
||||
export type DecodeRequestQueryVariables = {
|
||||
auth: AuthType;
|
||||
request: Scalars['String'];
|
||||
};
|
||||
|
||||
export type DecodeRequestQuery = { __typename?: 'Query' } & {
|
||||
decodeRequest?: Maybe<
|
||||
{ __typename?: 'decodeType' } & Pick<
|
||||
DecodeType,
|
||||
| 'chain_address'
|
||||
| 'cltv_delta'
|
||||
| 'description'
|
||||
| 'description_hash'
|
||||
| 'destination'
|
||||
| 'expires_at'
|
||||
| 'id'
|
||||
| 'tokens'
|
||||
> & {
|
||||
routes?: Maybe<
|
||||
Array<
|
||||
Maybe<
|
||||
{ __typename?: 'DecodeRoutesType' } & Pick<
|
||||
DecodeRoutesType,
|
||||
| 'base_fee_mtokens'
|
||||
| 'channel'
|
||||
| 'cltv_delta'
|
||||
| 'fee_rate'
|
||||
| 'public_key'
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
export type GetPendingChannelsQueryVariables = {
|
||||
auth: AuthType;
|
||||
};
|
||||
@ -1802,9 +1831,9 @@ export type OpenChannelMutationOptions = ApolloReactCommon.BaseMutationOptions<
|
||||
OpenChannelMutationVariables
|
||||
>;
|
||||
export const PayInvoiceDocument = gql`
|
||||
mutation PayInvoice($request: String!, $auth: authType!) {
|
||||
pay(request: $request, auth: $auth) {
|
||||
isConfirmed
|
||||
mutation PayInvoice($request: String!, $auth: authType!, $tokens: Int) {
|
||||
pay(request: $request, auth: $auth, tokens: $tokens) {
|
||||
is_confirmed
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1828,6 +1857,7 @@ export type PayInvoiceMutationFn = ApolloReactCommon.MutationFunction<
|
||||
* variables: {
|
||||
* request: // value for 'request'
|
||||
* auth: // value for 'auth'
|
||||
* tokens: // value for 'tokens'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
@ -2025,71 +2055,6 @@ export type PayAddressMutationOptions = ApolloReactCommon.BaseMutationOptions<
|
||||
PayAddressMutation,
|
||||
PayAddressMutationVariables
|
||||
>;
|
||||
export const DecodeRequestDocument = gql`
|
||||
mutation DecodeRequest($auth: authType!, $request: String!) {
|
||||
decodeRequest(auth: $auth, request: $request) {
|
||||
chainAddress
|
||||
cltvDelta
|
||||
description
|
||||
descriptionHash
|
||||
destination
|
||||
expiresAt
|
||||
id
|
||||
routes {
|
||||
baseFeeMTokens
|
||||
channel
|
||||
cltvDelta
|
||||
feeRate
|
||||
publicKey
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type DecodeRequestMutationFn = ApolloReactCommon.MutationFunction<
|
||||
DecodeRequestMutation,
|
||||
DecodeRequestMutationVariables
|
||||
>;
|
||||
|
||||
/**
|
||||
* __useDecodeRequestMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useDecodeRequestMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useDecodeRequestMutation` 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 [decodeRequestMutation, { data, loading, error }] = useDecodeRequestMutation({
|
||||
* variables: {
|
||||
* auth: // value for 'auth'
|
||||
* request: // value for 'request'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDecodeRequestMutation(
|
||||
baseOptions?: ApolloReactHooks.MutationHookOptions<
|
||||
DecodeRequestMutation,
|
||||
DecodeRequestMutationVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useMutation<
|
||||
DecodeRequestMutation,
|
||||
DecodeRequestMutationVariables
|
||||
>(DecodeRequestDocument, baseOptions);
|
||||
}
|
||||
export type DecodeRequestMutationHookResult = ReturnType<
|
||||
typeof useDecodeRequestMutation
|
||||
>;
|
||||
export type DecodeRequestMutationResult = ApolloReactCommon.MutationResult<
|
||||
DecodeRequestMutation
|
||||
>;
|
||||
export type DecodeRequestMutationOptions = ApolloReactCommon.BaseMutationOptions<
|
||||
DecodeRequestMutation,
|
||||
DecodeRequestMutationVariables
|
||||
>;
|
||||
export const UpdateFeesDocument = gql`
|
||||
mutation UpdateFees(
|
||||
$auth: authType!
|
||||
@ -2699,6 +2664,143 @@ export type GetChannelsQueryResult = ApolloReactCommon.QueryResult<
|
||||
GetChannelsQuery,
|
||||
GetChannelsQueryVariables
|
||||
>;
|
||||
export const GetNodeDocument = gql`
|
||||
query GetNode(
|
||||
$auth: authType!
|
||||
$publicKey: String!
|
||||
$withoutChannels: Boolean
|
||||
) {
|
||||
getNode(
|
||||
auth: $auth
|
||||
publicKey: $publicKey
|
||||
withoutChannels: $withoutChannels
|
||||
) {
|
||||
alias
|
||||
capacity
|
||||
channel_count
|
||||
color
|
||||
updated_at
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetNodeQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useGetNodeQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetNodeQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useGetNodeQuery({
|
||||
* variables: {
|
||||
* auth: // value for 'auth'
|
||||
* publicKey: // value for 'publicKey'
|
||||
* withoutChannels: // value for 'withoutChannels'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetNodeQuery(
|
||||
baseOptions?: ApolloReactHooks.QueryHookOptions<
|
||||
GetNodeQuery,
|
||||
GetNodeQueryVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useQuery<GetNodeQuery, GetNodeQueryVariables>(
|
||||
GetNodeDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export function useGetNodeLazyQuery(
|
||||
baseOptions?: ApolloReactHooks.LazyQueryHookOptions<
|
||||
GetNodeQuery,
|
||||
GetNodeQueryVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useLazyQuery<GetNodeQuery, GetNodeQueryVariables>(
|
||||
GetNodeDocument,
|
||||
baseOptions
|
||||
);
|
||||
}
|
||||
export type GetNodeQueryHookResult = ReturnType<typeof useGetNodeQuery>;
|
||||
export type GetNodeLazyQueryHookResult = ReturnType<typeof useGetNodeLazyQuery>;
|
||||
export type GetNodeQueryResult = ApolloReactCommon.QueryResult<
|
||||
GetNodeQuery,
|
||||
GetNodeQueryVariables
|
||||
>;
|
||||
export const DecodeRequestDocument = gql`
|
||||
query DecodeRequest($auth: authType!, $request: String!) {
|
||||
decodeRequest(auth: $auth, request: $request) {
|
||||
chain_address
|
||||
cltv_delta
|
||||
description
|
||||
description_hash
|
||||
destination
|
||||
expires_at
|
||||
id
|
||||
routes {
|
||||
base_fee_mtokens
|
||||
channel
|
||||
cltv_delta
|
||||
fee_rate
|
||||
public_key
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useDecodeRequestQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useDecodeRequestQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useDecodeRequestQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useDecodeRequestQuery({
|
||||
* variables: {
|
||||
* auth: // value for 'auth'
|
||||
* request: // value for 'request'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDecodeRequestQuery(
|
||||
baseOptions?: ApolloReactHooks.QueryHookOptions<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useQuery<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>(DecodeRequestDocument, baseOptions);
|
||||
}
|
||||
export function useDecodeRequestLazyQuery(
|
||||
baseOptions?: ApolloReactHooks.LazyQueryHookOptions<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>
|
||||
) {
|
||||
return ApolloReactHooks.useLazyQuery<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>(DecodeRequestDocument, baseOptions);
|
||||
}
|
||||
export type DecodeRequestQueryHookResult = ReturnType<
|
||||
typeof useDecodeRequestQuery
|
||||
>;
|
||||
export type DecodeRequestLazyQueryHookResult = ReturnType<
|
||||
typeof useDecodeRequestLazyQuery
|
||||
>;
|
||||
export type DecodeRequestQueryResult = ApolloReactCommon.QueryResult<
|
||||
DecodeRequestQuery,
|
||||
DecodeRequestQueryVariables
|
||||
>;
|
||||
export const GetPendingChannelsDocument = gql`
|
||||
query GetPendingChannels($auth: authType!) {
|
||||
getPendingChannels(auth: $auth) {
|
||||
|
@ -43,9 +43,9 @@ export const OPEN_CHANNEL = gql`
|
||||
`;
|
||||
|
||||
export const PAY_INVOICE = gql`
|
||||
mutation PayInvoice($request: String!, $auth: authType!) {
|
||||
pay(request: $request, auth: $auth) {
|
||||
isConfirmed
|
||||
mutation PayInvoice($request: String!, $auth: authType!, $tokens: Int) {
|
||||
pay(request: $request, auth: $auth, tokens: $tokens) {
|
||||
is_confirmed
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -90,28 +90,6 @@ export const PAY_ADDRESS = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const DECODE_REQUEST = gql`
|
||||
mutation DecodeRequest($auth: authType!, $request: String!) {
|
||||
decodeRequest(auth: $auth, request: $request) {
|
||||
chainAddress
|
||||
cltvDelta
|
||||
description
|
||||
descriptionHash
|
||||
destination
|
||||
expiresAt
|
||||
id
|
||||
routes {
|
||||
baseFeeMTokens
|
||||
channel
|
||||
cltvDelta
|
||||
feeRate
|
||||
publicKey
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_FEES = gql`
|
||||
mutation UpdateFees(
|
||||
$auth: authType!
|
||||
|
@ -105,6 +105,48 @@ export const GET_CHANNELS = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_NODE = gql`
|
||||
query GetNode(
|
||||
$auth: authType!
|
||||
$publicKey: String!
|
||||
$withoutChannels: Boolean
|
||||
) {
|
||||
getNode(
|
||||
auth: $auth
|
||||
publicKey: $publicKey
|
||||
withoutChannels: $withoutChannels
|
||||
) {
|
||||
alias
|
||||
capacity
|
||||
channel_count
|
||||
color
|
||||
updated_at
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DECODE_REQUEST = gql`
|
||||
query DecodeRequest($auth: authType!, $request: String!) {
|
||||
decodeRequest(auth: $auth, request: $request) {
|
||||
chain_address
|
||||
cltv_delta
|
||||
description
|
||||
description_hash
|
||||
destination
|
||||
expires_at
|
||||
id
|
||||
routes {
|
||||
base_fee_mtokens
|
||||
channel
|
||||
cltv_delta
|
||||
fee_rate
|
||||
public_key
|
||||
}
|
||||
tokens
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_PENDING_CHANNELS = gql`
|
||||
query GetPendingChannels($auth: authType!) {
|
||||
getPendingChannels(auth: $auth) {
|
||||
|
@ -12,7 +12,7 @@ import {
|
||||
Anchor,
|
||||
Circle,
|
||||
} from '../../../components/generic/Icons';
|
||||
import { getTooltipType } from '../../../components/generic/Helpers';
|
||||
import { getTooltipType } from '../../../components/generic/helpers';
|
||||
import { useAccount } from '../../../context/AccountContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../../../utils/error';
|
||||
|
@ -81,3 +81,18 @@ export const encode = (data: string): string =>
|
||||
Buffer.from(data, 'binary').toString('base64');
|
||||
export const decode = (data: string): string =>
|
||||
Buffer.from(data, 'base64').toString('binary');
|
||||
|
||||
export const isLightningInvoice = (invoice: string): boolean => {
|
||||
let isValidLightningInvoice = false;
|
||||
if (
|
||||
invoice.toLowerCase().startsWith('lightning:lnb') ||
|
||||
invoice.toLowerCase().startsWith('lnb')
|
||||
) {
|
||||
isValidLightningInvoice = true;
|
||||
}
|
||||
return isValidLightningInvoice;
|
||||
};
|
||||
|
||||
export const cleanLightningInvoice = invoice => {
|
||||
return invoice.replace('LIGHTNING:', '').replace('lightning:', '');
|
||||
};
|
@ -7,7 +7,7 @@ import {
|
||||
} from '../../components/generic/Styled';
|
||||
import { MainInfo } from '../../components/generic/CardGeneric';
|
||||
import { Circle, ChevronRight, XSvg } from '../../components/generic/Icons';
|
||||
import { getPercent } from '../../utils/Helpers';
|
||||
import { getPercent } from '../../utils/helpers';
|
||||
import { Price } from '../../components/price/Price';
|
||||
import { themeColors, chartColors } from '../../styles/Themes';
|
||||
import {
|
||||
|
@ -8,13 +8,13 @@ import {
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../../utils/error';
|
||||
import { themeColors, chartColors } from '../../styles/Themes';
|
||||
import { renderLine } from '../../components/generic/Helpers';
|
||||
import { renderLine } from '../../components/generic/helpers';
|
||||
import { HopCard } from './Balance.styled';
|
||||
import { SecureButton } from '../../components/buttons/secureButton/SecureButton';
|
||||
import { GET_ROUTES } from '../../graphql/query';
|
||||
import { ColorButton } from '../../components/buttons/colorButton/ColorButton';
|
||||
import { Price } from '../../components/price/Price';
|
||||
import { getPercent } from '../../utils/Helpers';
|
||||
import { getPercent } from '../../utils/helpers';
|
||||
import { AdminSwitch } from '../../components/adminSwitch/AdminSwitch';
|
||||
import {
|
||||
usePayViaRouteMutation,
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
getDateDif,
|
||||
getFormatDate,
|
||||
renderLine,
|
||||
} from '../../../components/generic/Helpers';
|
||||
} from '../../../components/generic/helpers';
|
||||
import styled from 'styled-components';
|
||||
import { getPrice } from '../../../components/price/Price';
|
||||
import { useSettings } from '../../../context/SettingsContext';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { Separation, SubCard } from '../../../components/generic/Styled';
|
||||
import { MainInfo } from '../../../components/generic/CardGeneric';
|
||||
import { renderLine } from '../../../components/generic/Helpers';
|
||||
import { renderLine } from '../../../components/generic/helpers';
|
||||
import { getPrice } from '../../../components/price/Price';
|
||||
import { useSettings } from '../../../context/SettingsContext';
|
||||
import { usePriceState } from '../../../context/PriceContext';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { getPercent } from '../../../utils/Helpers';
|
||||
import { getPercent } from '../../../utils/helpers';
|
||||
import {
|
||||
Progress,
|
||||
ProgressBar,
|
||||
@ -26,7 +26,7 @@ import {
|
||||
renderLine,
|
||||
getTransactionLink,
|
||||
getNodeLink,
|
||||
} from '../../../components/generic/Helpers';
|
||||
} from '../../../components/generic/helpers';
|
||||
import Modal from '../../../components/modal/ReactModal';
|
||||
import { CloseChannel } from '../../../components/modal/closeChannel/CloseChannel';
|
||||
import styled from 'styled-components';
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
getTransactionLink,
|
||||
renderLine,
|
||||
getNodeLink,
|
||||
} from '../../../components/generic/Helpers';
|
||||
} from '../../../components/generic/helpers';
|
||||
import styled from 'styled-components';
|
||||
import { Price } from '../../../components/price/Price';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { getPercent } from '../../../utils/Helpers';
|
||||
import { getPercent } from '../../../utils/helpers';
|
||||
import {
|
||||
Progress,
|
||||
ProgressBar,
|
||||
@ -25,7 +25,7 @@ import {
|
||||
getDateDif,
|
||||
getFormatDate,
|
||||
getNodeLink,
|
||||
} from '../../../components/generic/Helpers';
|
||||
} from '../../../components/generic/helpers';
|
||||
import { getPrice } from '../../../components/price/Price';
|
||||
import { usePriceState } from '../../../context/PriceContext';
|
||||
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
ResponsiveLine,
|
||||
NoWrapTitle,
|
||||
} from '../../components/generic/Styled';
|
||||
import { renderLine } from '../../components/generic/Helpers';
|
||||
import { renderLine } from '../../components/generic/helpers';
|
||||
import {
|
||||
MainInfo,
|
||||
NodeTitle,
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
getDateDif,
|
||||
getFormatDate,
|
||||
renderLine,
|
||||
} from '../../components/generic/Helpers';
|
||||
} from '../../components/generic/helpers';
|
||||
import { Price } from '../../components/price/Price';
|
||||
|
||||
interface ForwardCardProps {
|
||||
|
140
src/views/home/account/pay/Modals.tsx
Normal file
140
src/views/home/account/pay/Modals.tsx
Normal file
@ -0,0 +1,140 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
useDecodeRequestQuery,
|
||||
useGetNodeQuery,
|
||||
} from '../../../../generated/graphql';
|
||||
import {
|
||||
SingleLine,
|
||||
SubTitle,
|
||||
Separation,
|
||||
DarkSubTitle,
|
||||
} from '../../../../components/generic/Styled';
|
||||
import { Price } from '../../../../components/price/Price';
|
||||
import {
|
||||
renderLine,
|
||||
getNodeLink,
|
||||
getDateDif,
|
||||
getFormatDate,
|
||||
} from '../../../../components/generic/helpers';
|
||||
import { LoadingCard } from '../../../../components/loading/LoadingCard';
|
||||
import { Input } from '../../../../components/input/Input';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const WithMargin = styled.div`
|
||||
margin-right: 4px;
|
||||
`;
|
||||
|
||||
export const Centered = styled.div`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
interface DecodeProps {
|
||||
request: string;
|
||||
auth: {};
|
||||
}
|
||||
|
||||
export const RequestModal: React.FC<DecodeProps> = ({
|
||||
children,
|
||||
request,
|
||||
auth,
|
||||
}) => {
|
||||
const { data, loading, error } = useDecodeRequestQuery({
|
||||
variables: { auth, request },
|
||||
});
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Centered>
|
||||
<SubTitle>Error decoding the Invoice</SubTitle>
|
||||
<DarkSubTitle>
|
||||
Please verify you have correctly copied the Invoice.
|
||||
</DarkSubTitle>
|
||||
</Centered>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading || !data || !data.decodeRequest) {
|
||||
return <LoadingCard noCard={true} />;
|
||||
}
|
||||
|
||||
const { description, destination, expires_at, tokens } = data.decodeRequest;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<SubTitle>Pay Invoice</SubTitle>
|
||||
<Price amount={tokens} />
|
||||
</SingleLine>
|
||||
<Separation />
|
||||
{renderLine('Description:', description)}
|
||||
{renderLine('Destination:', getNodeLink(destination))}
|
||||
{renderLine(
|
||||
'Expires in:',
|
||||
`${getDateDif(expires_at)} (${getFormatDate(expires_at)})`
|
||||
)}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
interface KeysendProps {
|
||||
tokens: number;
|
||||
auth: {};
|
||||
publicKey: string;
|
||||
setTokens: (amount: number) => void;
|
||||
}
|
||||
|
||||
export const KeysendModal: React.FC<KeysendProps> = ({
|
||||
children,
|
||||
auth,
|
||||
publicKey,
|
||||
tokens,
|
||||
setTokens,
|
||||
}) => {
|
||||
const { data, loading, error } = useGetNodeQuery({
|
||||
variables: { auth, publicKey },
|
||||
});
|
||||
if (error) {
|
||||
return (
|
||||
<Centered>
|
||||
<SubTitle>Error getting node with that public key.</SubTitle>
|
||||
<DarkSubTitle>
|
||||
Please verify you copied the public key correctly.
|
||||
</DarkSubTitle>
|
||||
</Centered>
|
||||
);
|
||||
}
|
||||
if (loading || !data || !data.getNode) {
|
||||
return <LoadingCard noCard={true} />;
|
||||
}
|
||||
|
||||
const { alias } = data.getNode;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<SubTitle>Pay Node</SubTitle>
|
||||
<div>{alias}</div>
|
||||
</SingleLine>
|
||||
<Separation />
|
||||
<SingleLine>
|
||||
<SingleLine>
|
||||
<WithMargin>Sats:</WithMargin>
|
||||
<DarkSubTitle>
|
||||
<Price amount={tokens} />
|
||||
</DarkSubTitle>
|
||||
</SingleLine>
|
||||
<Input
|
||||
placeholder={'Sats'}
|
||||
withMargin={'0 0 0 8px'}
|
||||
type={'number'}
|
||||
onChange={e => setTokens(Number(e.target.value))}
|
||||
/>
|
||||
</SingleLine>
|
||||
<DarkSubTitle withMargin={'16px 0'}>
|
||||
Remember Keysend is an experimental feature. Use at your own risk.
|
||||
</DarkSubTitle>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
};
|
@ -5,6 +5,8 @@ import {
|
||||
SubTitle,
|
||||
Separation,
|
||||
SingleLine,
|
||||
NoWrapTitle,
|
||||
DarkSubTitle,
|
||||
} from '../../../../components/generic/Styled';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../../../../utils/error';
|
||||
@ -16,62 +18,109 @@ import { ColorButton } from '../../../../components/buttons/colorButton/ColorBut
|
||||
import {
|
||||
renderLine,
|
||||
getNodeLink,
|
||||
} from '../../../../components/generic/Helpers';
|
||||
} from '../../../../components/generic/helpers';
|
||||
import { Price } from '../../../../components/price/Price';
|
||||
import {
|
||||
usePayInvoiceMutation,
|
||||
useDecodeRequestMutation,
|
||||
useGetNodeLazyQuery,
|
||||
useDecodeRequestQuery,
|
||||
} from '../../../../generated/graphql';
|
||||
import { useStatusState } from '../../../../context/StatusContext';
|
||||
import {
|
||||
isLightningInvoice,
|
||||
cleanLightningInvoice,
|
||||
} from '../../../../utils/helpers';
|
||||
import { KeysendModal, RequestModal } from './Modals';
|
||||
|
||||
export const PayCard = ({ setOpen }: { setOpen: () => void }) => {
|
||||
const [request, setRequest] = useState('');
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [request, setRequest] = useState<string>('');
|
||||
const [tokens, setTokens] = useState<number>(0);
|
||||
const [modalType, setModalType] = useState('none');
|
||||
|
||||
const { auth } = useAccount();
|
||||
const { minorVersion } = useStatusState();
|
||||
|
||||
const canKeysend = minorVersion >= 9;
|
||||
|
||||
const [makePayment, { loading }] = usePayInvoiceMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
onCompleted: () => {
|
||||
toast.success('Payment Sent');
|
||||
setRequest('');
|
||||
setModalOpen(false);
|
||||
setTokens(0);
|
||||
setModalType('none');
|
||||
setOpen();
|
||||
},
|
||||
});
|
||||
|
||||
const [decode, { data, loading: decodeLoading }] = useDecodeRequestMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
const handleClick = () => {
|
||||
const isRequest = isLightningInvoice(request);
|
||||
|
||||
useEffect(() => {
|
||||
if (data && data.decodeRequest) setModalOpen(true);
|
||||
}, [data, setModalOpen]);
|
||||
|
||||
const renderData = () => {
|
||||
if (!data || !data.decodeRequest) return null;
|
||||
|
||||
const { description, destination, expiresAt, tokens } = data.decodeRequest;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SingleLine>
|
||||
<SubTitle>Pay Invoice</SubTitle>
|
||||
<Price amount={tokens} />
|
||||
</SingleLine>
|
||||
<Separation />
|
||||
{renderLine('Description:', description)}
|
||||
{renderLine('Destination:', getNodeLink(destination))}
|
||||
{renderLine('Expires At:', expiresAt)}
|
||||
</>
|
||||
);
|
||||
if (!isRequest && canKeysend) {
|
||||
setModalType('keysend');
|
||||
} else {
|
||||
if (!isRequest) {
|
||||
toast.error('Invalid Invoice');
|
||||
return;
|
||||
}
|
||||
setModalType('request');
|
||||
}
|
||||
};
|
||||
|
||||
const renderModal = () => {
|
||||
if (modalType === 'request') {
|
||||
return (
|
||||
<RequestModal request={request} auth={auth}>
|
||||
{renderButton()}
|
||||
</RequestModal>
|
||||
);
|
||||
}
|
||||
if (modalType === 'keysend') {
|
||||
return (
|
||||
<KeysendModal
|
||||
tokens={tokens}
|
||||
auth={auth}
|
||||
publicKey={request}
|
||||
setTokens={setTokens}
|
||||
>
|
||||
{renderButton()}
|
||||
</KeysendModal>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const renderButton = () => (
|
||||
<SecureButton
|
||||
callback={makePayment}
|
||||
variables={
|
||||
modalType === 'none'
|
||||
? { request: cleanLightningInvoice(request) }
|
||||
: { request, tokens }
|
||||
}
|
||||
disabled={
|
||||
modalType === 'request' ? request === '' : request === '' || tokens <= 0
|
||||
}
|
||||
withMargin={'16px 0 0'}
|
||||
loading={loading}
|
||||
fullWidth={true}
|
||||
>
|
||||
Send
|
||||
</SecureButton>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResponsiveLine>
|
||||
<Sub4Title>Invoice:</Sub4Title>
|
||||
<NoWrapTitle>
|
||||
<Sub4Title as={'div'}>
|
||||
{canKeysend ? 'Invoice or Public Key:' : 'Invoice:'}
|
||||
</Sub4Title>
|
||||
</NoWrapTitle>
|
||||
<Input
|
||||
placeholder={'Lightning Invoice'}
|
||||
placeholder={
|
||||
canKeysend ? 'Lightning Invoice or Public Key' : 'Invoice'
|
||||
}
|
||||
withMargin={'0 0 0 24px'}
|
||||
mobileMargin={'0 0 16px'}
|
||||
onChange={e => setRequest(e.target.value)}
|
||||
@ -80,28 +129,21 @@ export const PayCard = ({ setOpen }: { setOpen: () => void }) => {
|
||||
disabled={request === ''}
|
||||
withMargin={'0 0 0 16px'}
|
||||
mobileMargin={'0'}
|
||||
loading={decodeLoading}
|
||||
mobileFullWidth={true}
|
||||
onClick={() => {
|
||||
decode({ variables: { request, auth } });
|
||||
}}
|
||||
onClick={() => handleClick()}
|
||||
>
|
||||
Send Sats
|
||||
</ColorButton>
|
||||
</ResponsiveLine>
|
||||
<Modal isOpen={modalOpen} closeCallback={() => setModalOpen(false)}>
|
||||
{renderData()}
|
||||
<SecureButton
|
||||
callback={makePayment}
|
||||
variables={{ request }}
|
||||
disabled={request === ''}
|
||||
withMargin={'16px 0 0'}
|
||||
loading={loading}
|
||||
arrow={true}
|
||||
fullWidth={true}
|
||||
>
|
||||
Send
|
||||
</SecureButton>
|
||||
<Modal
|
||||
isOpen={modalType !== 'none'}
|
||||
closeCallback={() => {
|
||||
setModalType('none');
|
||||
setTokens(0);
|
||||
setRequest('');
|
||||
}}
|
||||
>
|
||||
{renderModal()}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
@ -22,7 +22,7 @@ import { mediaWidths } from '../../../../styles/Themes';
|
||||
import { useSettings } from '../../../../context/SettingsContext';
|
||||
import Modal from '../../../../components/modal/ReactModal';
|
||||
import { ColorButton } from '../../../../components/buttons/colorButton/ColorButton';
|
||||
import { renderLine } from '../../../../components/generic/Helpers';
|
||||
import { renderLine } from '../../../../components/generic/helpers';
|
||||
import { usePriceState } from '../../../../context/PriceContext';
|
||||
import { usePayAddressMutation } from '../../../../generated/graphql';
|
||||
|
||||
|
@ -2,88 +2,45 @@ import React, { useState } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Sub4Title,
|
||||
Separation,
|
||||
ResponsiveLine,
|
||||
} from '../../../../components/generic/Styled';
|
||||
import {
|
||||
renderLine,
|
||||
getNodeLink,
|
||||
} from '../../../../components/generic/Helpers';
|
||||
import { useAccount } from '../../../../context/AccountContext';
|
||||
import { getErrorContent } from '../../../../utils/error';
|
||||
import { toast } from 'react-toastify';
|
||||
import { ColorButton } from '../../../../components/buttons/colorButton/ColorButton';
|
||||
import { Input } from '../../../../components/input/Input';
|
||||
import { Price } from '../../../../components/price/Price';
|
||||
import { useDecodeRequestMutation } from '../../../../generated/graphql';
|
||||
import { Decoded } from './Decoded';
|
||||
|
||||
export const DecodeCard = ({ color }: { color: string }) => {
|
||||
const [request, setRequest] = useState('');
|
||||
|
||||
const { auth } = useAccount();
|
||||
|
||||
const [decode, { data, loading }] = useDecodeRequestMutation({
|
||||
onError: error => toast.error(getErrorContent(error)),
|
||||
});
|
||||
|
||||
const renderData = () => {
|
||||
if (!data || !data.decodeRequest) return null;
|
||||
|
||||
const {
|
||||
chainAddress,
|
||||
cltvDelta,
|
||||
description,
|
||||
descriptionHash,
|
||||
destination,
|
||||
expiresAt,
|
||||
id,
|
||||
tokens,
|
||||
} = data.decodeRequest;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Separation />
|
||||
{renderLine('Id:', id)}
|
||||
{renderLine('Destination:', getNodeLink(destination))}
|
||||
{renderLine('Description:', description)}
|
||||
{renderLine('Description Hash:', descriptionHash)}
|
||||
{renderLine('Chain Address:', chainAddress)}
|
||||
{renderLine('CLTV Delta:', cltvDelta)}
|
||||
{renderLine('Expires At:', expiresAt)}
|
||||
{renderLine('Amount:', <Price amount={tokens} />)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<Card bottom={'20px'}>
|
||||
<ResponsiveLine>
|
||||
<Sub4Title>Request:</Sub4Title>
|
||||
<Input
|
||||
placeholder={'Lightning Invoice'}
|
||||
withMargin={'0 0 0 24px'}
|
||||
mobileMargin={'0 0 16px'}
|
||||
color={color}
|
||||
value={request}
|
||||
onChange={e => setRequest(e.target.value)}
|
||||
/>
|
||||
<ColorButton
|
||||
color={color}
|
||||
disabled={request === ''}
|
||||
withMargin={'0 0 0 16px'}
|
||||
mobileMargin={'0'}
|
||||
arrow={true}
|
||||
loading={loading}
|
||||
mobileFullWidth={true}
|
||||
onClick={() => {
|
||||
setRequest('');
|
||||
decode({ variables: { request, auth } });
|
||||
}}
|
||||
>
|
||||
Decode
|
||||
</ColorButton>
|
||||
</ResponsiveLine>
|
||||
{renderData()}
|
||||
{!show && (
|
||||
<ResponsiveLine>
|
||||
<Sub4Title>Request:</Sub4Title>
|
||||
<Input
|
||||
placeholder={'Lightning Invoice'}
|
||||
withMargin={'0 0 0 24px'}
|
||||
mobileMargin={'0 0 16px'}
|
||||
color={color}
|
||||
value={request}
|
||||
onChange={e => setRequest(e.target.value)}
|
||||
/>
|
||||
<ColorButton
|
||||
color={color}
|
||||
disabled={request === ''}
|
||||
withMargin={'0 0 0 16px'}
|
||||
mobileMargin={'0'}
|
||||
arrow={true}
|
||||
mobileFullWidth={true}
|
||||
onClick={() => {
|
||||
setShow(true);
|
||||
}}
|
||||
>
|
||||
Decode
|
||||
</ColorButton>
|
||||
</ResponsiveLine>
|
||||
)}
|
||||
{show && <Decoded request={request} setShow={setShow} />}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
61
src/views/home/quickActions/decode/Decoded.tsx
Normal file
61
src/views/home/quickActions/decode/Decoded.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import * as React from 'react';
|
||||
import { useAccount } from '../../../../context/AccountContext';
|
||||
import { getErrorContent } from '../../../../utils/error';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useDecodeRequestQuery } from '../../../../generated/graphql';
|
||||
import { LoadingCard } from '../../../../components/loading/LoadingCard';
|
||||
import {
|
||||
renderLine,
|
||||
getNodeLink,
|
||||
getDateDif,
|
||||
getFormatDate,
|
||||
} from '../../../../components/generic/helpers';
|
||||
import { Price } from '../../../../components/price/Price';
|
||||
|
||||
interface DecodedProps {
|
||||
request: string;
|
||||
setShow: (show: boolean) => void;
|
||||
}
|
||||
|
||||
export const Decoded = ({ request, setShow }: DecodedProps) => {
|
||||
const { auth } = useAccount();
|
||||
|
||||
const { data, loading } = useDecodeRequestQuery({
|
||||
variables: { auth, request },
|
||||
onError: error => {
|
||||
setShow(false);
|
||||
toast.error(getErrorContent(error));
|
||||
},
|
||||
});
|
||||
|
||||
if (loading || !data || !data.decodeRequest) {
|
||||
return <LoadingCard noCard={true} />;
|
||||
}
|
||||
|
||||
const {
|
||||
chain_address,
|
||||
cltv_delta,
|
||||
description,
|
||||
description_hash,
|
||||
destination,
|
||||
expires_at,
|
||||
id,
|
||||
tokens,
|
||||
} = data.decodeRequest;
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderLine('Id:', id)}
|
||||
{renderLine('Destination:', getNodeLink(destination))}
|
||||
{renderLine('Description:', description)}
|
||||
{renderLine('Description Hash:', description_hash)}
|
||||
{renderLine('Chain Address:', chain_address)}
|
||||
{renderLine('CLTV Delta:', cltv_delta)}
|
||||
{renderLine(
|
||||
'Expires in:',
|
||||
`${getDateDif(expires_at)} (${getFormatDate(expires_at)})`
|
||||
)}
|
||||
{renderLine('Amount:', <Price amount={tokens} />)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -14,7 +14,7 @@ import {
|
||||
getFormatDate,
|
||||
getTooltipType,
|
||||
getNodeLink,
|
||||
} from '../../components/generic/Helpers';
|
||||
} from '../../components/generic/helpers';
|
||||
import styled from 'styled-components';
|
||||
import { DownArrow, UpArrow } from '../../components/generic/Icons';
|
||||
import {
|
||||
@ -23,7 +23,7 @@ import {
|
||||
NodeTitle,
|
||||
MainInfo,
|
||||
} from '../../components/generic/CardGeneric';
|
||||
import { getPercent } from '../../utils/Helpers';
|
||||
import { getPercent } from '../../utils/helpers';
|
||||
import { useSettings } from '../../context/SettingsContext';
|
||||
import ReactTooltip from 'react-tooltip';
|
||||
import { usePriceState } from '../../context/PriceContext';
|
||||
|
@ -13,7 +13,7 @@ import { DownloadBackups } from './DownloadBackups';
|
||||
import { VerifyBackups } from './VerifyBackups';
|
||||
import { RecoverFunds } from './RecoverFunds';
|
||||
import { AdminSwitch } from '../../../components/adminSwitch/AdminSwitch';
|
||||
import { getDateDif, getFormatDate } from '../../../components/generic/Helpers';
|
||||
import { getDateDif, getFormatDate } from '../../../components/generic/helpers';
|
||||
|
||||
export const BackupsView = () => {
|
||||
const [lastDate, setLastDate] = useState('');
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { DarkSubTitle, SingleLine } from '../../../components/generic/Styled';
|
||||
import { saveToPc } from '../../../utils/Helpers';
|
||||
import { saveToPc } from '../../../utils/helpers';
|
||||
import { useAccount } from '../../../context/AccountContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getErrorContent } from '../../../utils/error';
|
||||
|
@ -13,7 +13,7 @@ import { NoWrap } from './Messages';
|
||||
import { Input } from '../../../components/input/Input';
|
||||
import CopyToClipboard from 'react-copy-to-clipboard';
|
||||
import { Column, WrapRequest } from '../Tools.styled';
|
||||
import { getNodeLink } from '../../../components/generic/Helpers';
|
||||
import { getNodeLink } from '../../../components/generic/helpers';
|
||||
import { useVerifyMessageLazyQuery } from '../../../generated/graphql';
|
||||
|
||||
export const VerifyMessage = () => {
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
} from './OfferCard.styled';
|
||||
import { MainInfo } from '../../components/generic/CardGeneric';
|
||||
import { themeColors } from '../../styles/Themes';
|
||||
import { renderLine } from '../../components/generic/Helpers';
|
||||
import { renderLine } from '../../components/generic/helpers';
|
||||
import numeral from 'numeral';
|
||||
import { MethodBoxes } from './MethodBoxes';
|
||||
import { Link } from '../../components/link/Link';
|
||||
|
@ -15,10 +15,10 @@ import { FilterModal } from './Modal/FilterModal';
|
||||
import { SortOptions } from './OfferConfigs';
|
||||
import { QueryProps } from '../../../pages/trading';
|
||||
import { XSvg } from '../../components/generic/Icons';
|
||||
import { renderLine } from '../../components/generic/Helpers';
|
||||
import { renderLine } from '../../components/generic/helpers';
|
||||
import { chartColors } from '../../styles/Themes';
|
||||
import { useRouter } from 'next/router';
|
||||
import { encode } from '../../utils/Helpers';
|
||||
import { encode } from '../../utils/helpers';
|
||||
import { appendBasePath } from '../../utils/basePath';
|
||||
|
||||
type ActionType = {
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
getDateDif,
|
||||
getFormatDate,
|
||||
renderLine,
|
||||
} from '../../components/generic/Helpers';
|
||||
} from '../../components/generic/helpers';
|
||||
import { Price } from '../../components/price/Price';
|
||||
|
||||
interface InvoiceCardProps {
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
getFormatDate,
|
||||
getNodeLink,
|
||||
renderLine,
|
||||
} from '../../components/generic/Helpers';
|
||||
} from '../../components/generic/helpers';
|
||||
import styled from 'styled-components';
|
||||
import { Price } from '../../components/price/Price';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user