diff --git a/src/schemas/mutations/invoices/index.ts b/src/schemas/mutations/invoices/index.ts index f565bb3e..e07fd804 100644 --- a/src/schemas/mutations/invoices/index.ts +++ b/src/schemas/mutations/invoices/index.ts @@ -2,10 +2,12 @@ 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, }; diff --git a/src/schemas/mutations/invoices/payViaRoute.ts b/src/schemas/mutations/invoices/payViaRoute.ts new file mode 100644 index 00000000..e3f245d6 --- /dev/null +++ b/src/schemas/mutations/invoices/payViaRoute.ts @@ -0,0 +1,43 @@ +import { GraphQLNonNull, GraphQLBoolean, GraphQLString } from 'graphql'; +import { payViaRoutes, createInvoice } from 'ln-service'; +import { logger } from '../../../helpers/logger'; +import { requestLimiter } from '../../../helpers/rateLimiter'; +import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers'; +import { AuthType } from '../../../schemaTypes/Auth'; + +export const payViaRoute = { + type: GraphQLBoolean, + args: { + auth: { type: new GraphQLNonNull(AuthType) }, + route: { type: new GraphQLNonNull(GraphQLString) }, + }, + resolve: async (root: any, params: any, context: any) => { + await requestLimiter(context.ip, 'payViaRoute'); + + const lnd = getAuthLnd(params.auth); + + let route; + try { + route = JSON.parse(params.route); + } catch (error) { + logger.error('Corrupt route json: %o', error); + throw new Error('Corrupt Route JSON'); + } + + const { id } = await createInvoice({ + lnd, + tokens: params.tokens, + description: 'Balancing Channel', + }).catch((error: any) => { + logger.error('Error getting invoice: %o', error); + throw new Error(getErrorMsg(error)); + }); + + await payViaRoutes({ lnd, routes: [route], id }).catch((error: any) => { + logger.error('Error making payment: %o', error); + throw new Error(getErrorMsg(error)); + }); + + return true; + }, +}; diff --git a/src/schemas/query/channels/channels.ts b/src/schemas/query/channels/channels.ts index 5b536db0..b98ba928 100644 --- a/src/schemas/query/channels/channels.ts +++ b/src/schemas/query/channels/channels.ts @@ -1,4 +1,4 @@ -import { GraphQLList, GraphQLNonNull } from 'graphql'; +import { GraphQLList, GraphQLNonNull, GraphQLBoolean } from 'graphql'; import { getChannels as getLnChannels, getNode } from 'ln-service'; import { logger } from '../../../helpers/logger'; import { ChannelType } from '../../../schemaTypes/query/channels/channels'; @@ -38,7 +38,12 @@ interface ChannelProps { export const getChannels = { type: new GraphQLList(ChannelType), - args: { auth: { type: new GraphQLNonNull(AuthType) } }, + args: { + auth: { + type: new GraphQLNonNull(AuthType), + }, + active: { type: GraphQLBoolean }, + }, resolve: async (root: any, params: any, context: any) => { await requestLimiter(context.ip, 'channels'); @@ -47,6 +52,7 @@ export const getChannels = { try { const channelList: ChannelListProps = await getLnChannels({ lnd, + is_active: params.active, }); const getChannelList = () => diff --git a/src/schemas/query/index.ts b/src/schemas/query/index.ts index fd087236..15585466 100644 --- a/src/schemas/query/index.ts +++ b/src/schemas/query/index.ts @@ -5,6 +5,7 @@ import { dataQueries } from './data'; import { reportQueries } from './report'; import { flowQueries } from './flow'; import { backupQueries } from './backup'; +import { routeQueries } from './route'; export const query = { ...channelQueries, @@ -14,4 +15,5 @@ export const query = { ...reportQueries, ...flowQueries, ...backupQueries, + ...routeQueries, }; diff --git a/src/schemas/query/route/getRoutes.ts b/src/schemas/query/route/getRoutes.ts new file mode 100644 index 00000000..08e7015f --- /dev/null +++ b/src/schemas/query/route/getRoutes.ts @@ -0,0 +1,40 @@ +import { GraphQLNonNull, GraphQLString, GraphQLInt } from 'graphql'; +import { getRouteToDestination, getWalletInfo } from 'ln-service'; +import { logger } from '../../../helpers/logger'; +import { requestLimiter } from '../../../helpers/rateLimiter'; +import { getAuthLnd, getErrorMsg } from '../../../helpers/helpers'; +import { AuthType } from '../../../schemaTypes/Auth'; + +export const getRoutes = { + type: GraphQLString, + args: { + auth: { type: new GraphQLNonNull(AuthType) }, + outgoing: { type: new GraphQLNonNull(GraphQLString) }, + incoming: { type: new GraphQLNonNull(GraphQLString) }, + tokens: { type: new GraphQLNonNull(GraphQLInt) }, + }, + resolve: async (root: any, params: any, context: any) => { + await requestLimiter(context.ip, 'getRoutes'); + + const lnd = getAuthLnd(params.auth); + + const { public_key } = await getWalletInfo({ lnd }); + + const { route } = await getRouteToDestination({ + lnd, + outgoing_channel: params.outgoing, + incoming_peer: params.incoming, + destination: public_key, + tokens: params.tokens, + }).catch((error: any) => { + logger.error('Error getting routes: %o', error); + throw new Error(getErrorMsg(error)); + }); + + if (!route) { + throw new Error('No route found.'); + } + + return JSON.stringify(route); + }, +}; diff --git a/src/schemas/query/route/index.ts b/src/schemas/query/route/index.ts new file mode 100644 index 00000000..23e8e292 --- /dev/null +++ b/src/schemas/query/route/index.ts @@ -0,0 +1,5 @@ +import { getRoutes } from './getRoutes'; + +export const routeQueries = { + getRoutes, +}; diff --git a/src/utils/rateLimitConfig.ts b/src/utils/rateLimitConfig.ts index fadbb729..911686f7 100644 --- a/src/utils/rateLimitConfig.ts +++ b/src/utils/rateLimitConfig.ts @@ -38,4 +38,6 @@ export const RateConfig: RateConfigProps = { recoverFunds: { max: 3, window: '1s' }, updateFees: { max: 3, window: '1s' }, chainTransactions: { max: 3, window: '1s' }, + getRoutes: { max: 3, window: '1s' }, + payViaRoute: { max: 3, window: '1s' }, };