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:
Anthony Potdevin 2020-04-20 20:15:47 +02:00 committed by GitHub
parent f29ba3d2ae
commit b4473c50bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 795 additions and 418 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

@ -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: () => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,7 @@ import {
getDateDif,
getFormatDate,
renderLine,
} from '../../components/generic/Helpers';
} from '../../components/generic/helpers';
import { Price } from '../../components/price/Price';
interface ForwardCardProps {

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

View File

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

View File

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

View File

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

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

View File

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

View File

@ -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('');

View File

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

View File

@ -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 = () => {

View File

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

View File

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

View File

@ -16,7 +16,7 @@ import {
getDateDif,
getFormatDate,
renderLine,
} from '../../components/generic/Helpers';
} from '../../components/generic/helpers';
import { Price } from '../../components/price/Price';
interface InvoiceCardProps {

View File

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