mirror of
https://github.com/apotdevin/thunderhub.git
synced 2025-02-22 14:22:33 +01:00
feat: more queries and mutations
This commit is contained in:
parent
39fbfe3bbf
commit
9973c3c54f
22 changed files with 540 additions and 1 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "thunderhub",
|
"name": "thunderhub",
|
||||||
"version": "1.0.0",
|
"version": "0.0.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -224,6 +224,14 @@
|
||||||
"graphql": "^14.5.3"
|
"graphql": "^14.5.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/graphql-iso-date": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/graphql-iso-date/-/graphql-iso-date-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-lchvlAox/yqk2Rcrgqh+uvwc1UC9i1hap+0tqQqyYYcAica6Uw2D4mUkCNcw+WeZ8dvSS5QdtIlJuDYUf4nLXQ==",
|
||||||
|
"requires": {
|
||||||
|
"graphql": "^14.5.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/graphql-upload": {
|
"@types/graphql-upload": {
|
||||||
"version": "8.0.3",
|
"version": "8.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/graphql-upload/-/graphql-upload-8.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/graphql-upload/-/graphql-upload-8.0.3.tgz",
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
"homepage": "https://github.com/apotdevin/thunderhub#readme",
|
"homepage": "https://github.com/apotdevin/thunderhub#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/graphql-depth-limit": "^1.1.2",
|
"@types/graphql-depth-limit": "^1.1.2",
|
||||||
|
"@types/graphql-iso-date": "^3.3.3",
|
||||||
"apollo-server": "^2.9.7",
|
"apollo-server": "^2.9.7",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"graphql": "^14.5.8",
|
"graphql": "^14.5.8",
|
||||||
|
|
|
@ -6,6 +6,7 @@ import depthLimit from "graphql-depth-limit";
|
||||||
import lnService from "ln-service";
|
import lnService from "ln-service";
|
||||||
|
|
||||||
const { lnd } = lnService.authenticatedLndGrpc({
|
const { lnd } = lnService.authenticatedLndGrpc({
|
||||||
|
cert: process.env.LND_CERT,
|
||||||
macaroon: process.env.LND_MAC,
|
macaroon: process.env.LND_MAC,
|
||||||
socket: process.env.LND_IP
|
socket: process.env.LND_IP
|
||||||
});
|
});
|
||||||
|
|
15
src/schemaTypes/mutation.ts/channels/closeChannel.ts
Normal file
15
src/schemaTypes/mutation.ts/channels/closeChannel.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { GraphQLObjectType, GraphQLString } from "graphql";
|
||||||
|
|
||||||
|
export const CloseChannelType = new GraphQLObjectType({
|
||||||
|
name: "closeChannelType",
|
||||||
|
fields: () => {
|
||||||
|
return {
|
||||||
|
transactionId: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
|
transactionOutputIndex: {
|
||||||
|
type: GraphQLString
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
15
src/schemaTypes/mutation.ts/channels/openChannel.ts
Normal file
15
src/schemaTypes/mutation.ts/channels/openChannel.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { GraphQLObjectType, GraphQLString } from "graphql";
|
||||||
|
|
||||||
|
export const OpenChannelType = new GraphQLObjectType({
|
||||||
|
name: "openChannelType",
|
||||||
|
fields: () => {
|
||||||
|
return {
|
||||||
|
transactionId: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
|
transactionOutputIndex: {
|
||||||
|
type: GraphQLString
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
17
src/schemaTypes/mutation.ts/invoice/createInvoice.ts
Normal file
17
src/schemaTypes/mutation.ts/invoice/createInvoice.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { GraphQLObjectType, GraphQLString, GraphQLInt } from "graphql";
|
||||||
|
import { GraphQLDateTime } from "graphql-iso-date";
|
||||||
|
|
||||||
|
export const InvoiceType = new GraphQLObjectType({
|
||||||
|
name: "invoiceType",
|
||||||
|
fields: () => {
|
||||||
|
return {
|
||||||
|
chainAddress: { type: GraphQLString },
|
||||||
|
createdAt: { type: GraphQLDateTime },
|
||||||
|
description: { type: GraphQLString },
|
||||||
|
id: { type: GraphQLString },
|
||||||
|
request: { type: GraphQLString },
|
||||||
|
secret: { type: GraphQLString },
|
||||||
|
tokens: { type: GraphQLInt }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
40
src/schemaTypes/mutation.ts/invoice/parsePayment.ts
Normal file
40
src/schemaTypes/mutation.ts/invoice/parsePayment.ts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
GraphQLObjectType,
|
||||||
|
GraphQLString,
|
||||||
|
GraphQLInt,
|
||||||
|
GraphQLBoolean,
|
||||||
|
GraphQLList
|
||||||
|
} from "graphql";
|
||||||
|
import { GraphQLDateTime } from "graphql-iso-date";
|
||||||
|
|
||||||
|
const RouteType = new GraphQLObjectType({
|
||||||
|
name: "RouteType",
|
||||||
|
fields: () => ({
|
||||||
|
mTokenFee: { type: GraphQLString },
|
||||||
|
channel: { type: GraphQLString },
|
||||||
|
cltvDelta: { type: GraphQLInt },
|
||||||
|
feeRate: { type: GraphQLInt },
|
||||||
|
publicKey: { type: GraphQLString }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const ParsePaymentType = new GraphQLObjectType({
|
||||||
|
name: "parsePaymentType",
|
||||||
|
fields: () => {
|
||||||
|
return {
|
||||||
|
chainAddresses: { type: new GraphQLList(GraphQLString) },
|
||||||
|
cltvDelta: { type: GraphQLInt },
|
||||||
|
createdAt: { type: GraphQLDateTime },
|
||||||
|
description: { type: GraphQLString },
|
||||||
|
descriptionHash: { type: GraphQLString },
|
||||||
|
destination: { type: GraphQLString },
|
||||||
|
expiresAt: { type: GraphQLDateTime },
|
||||||
|
id: { type: GraphQLString },
|
||||||
|
isExpired: { type: GraphQLBoolean },
|
||||||
|
mTokens: { type: GraphQLString },
|
||||||
|
network: { type: GraphQLString },
|
||||||
|
routes: { type: new GraphQLList(RouteType) },
|
||||||
|
tokens: { type: GraphQLInt }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
35
src/schemaTypes/mutation.ts/invoice/pay.ts
Normal file
35
src/schemaTypes/mutation.ts/invoice/pay.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import {
|
||||||
|
GraphQLObjectType,
|
||||||
|
GraphQLString,
|
||||||
|
GraphQLInt,
|
||||||
|
GraphQLList,
|
||||||
|
GraphQLBoolean
|
||||||
|
} from "graphql";
|
||||||
|
|
||||||
|
const HopsType = new GraphQLObjectType({
|
||||||
|
name: "hopsType",
|
||||||
|
fields: () => ({
|
||||||
|
channel: { type: GraphQLString },
|
||||||
|
channelCapacity: { type: GraphQLInt },
|
||||||
|
mTokenFee: { type: GraphQLString },
|
||||||
|
forwardMTokens: { type: GraphQLString },
|
||||||
|
timeout: { type: GraphQLInt }
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
export const PayType = new GraphQLObjectType({
|
||||||
|
name: "payType",
|
||||||
|
fields: () => {
|
||||||
|
return {
|
||||||
|
fee: { type: GraphQLInt },
|
||||||
|
feeMTokens: { type: GraphQLString },
|
||||||
|
hops: { type: new GraphQLList(HopsType) },
|
||||||
|
id: { type: GraphQLString },
|
||||||
|
isConfirmed: { type: GraphQLBoolean },
|
||||||
|
isOutgoing: { type: GraphQLBoolean },
|
||||||
|
mtokens: { type: GraphQLString },
|
||||||
|
secret: { type: GraphQLString },
|
||||||
|
tokens: { type: GraphQLInt }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
|
@ -8,6 +8,9 @@ export const ChannelType = new GraphQLObjectType({
|
||||||
capacity: {
|
capacity: {
|
||||||
type: GraphQLInt
|
type: GraphQLInt
|
||||||
},
|
},
|
||||||
|
id: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
isActive: {
|
isActive: {
|
||||||
type: GraphQLBoolean
|
type: GraphQLBoolean
|
||||||
},
|
},
|
||||||
|
|
34
src/schemaTypes/query/info/networkInfo.ts
Normal file
34
src/schemaTypes/query/info/networkInfo.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { GraphQLObjectType, GraphQLString } from "graphql";
|
||||||
|
import { GraphQLInt } from "graphql";
|
||||||
|
|
||||||
|
export const NetworkInfoType = new GraphQLObjectType({
|
||||||
|
name: "networkInfoType",
|
||||||
|
fields: () => {
|
||||||
|
return {
|
||||||
|
averageChannelSize: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
|
channelCount: {
|
||||||
|
type: GraphQLInt
|
||||||
|
},
|
||||||
|
maxChannelSize: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
|
medianChannelSize: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
|
minChannelSize: {
|
||||||
|
type: GraphQLInt
|
||||||
|
},
|
||||||
|
nodeCount: {
|
||||||
|
type: GraphQLInt
|
||||||
|
},
|
||||||
|
notRecentlyUpdatedPolicyCount: {
|
||||||
|
type: GraphQLInt
|
||||||
|
},
|
||||||
|
totalCapacity: {
|
||||||
|
type: GraphQLString
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,9 +1,14 @@
|
||||||
import { GraphQLSchema, GraphQLObjectType } from "graphql";
|
import { GraphQLSchema, GraphQLObjectType } from "graphql";
|
||||||
import { query } from "./query";
|
import { query } from "./query";
|
||||||
|
import { mutation } from "./mutations";
|
||||||
|
|
||||||
export const thunderHubSchema = new GraphQLSchema({
|
export const thunderHubSchema = new GraphQLSchema({
|
||||||
query: new GraphQLObjectType({
|
query: new GraphQLObjectType({
|
||||||
name: "Query",
|
name: "Query",
|
||||||
fields: query
|
fields: query
|
||||||
|
}),
|
||||||
|
mutation: new GraphQLObjectType({
|
||||||
|
name: "Mutation",
|
||||||
|
fields: mutation
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
53
src/schemas/mutations/channels/closeChannel.ts
Normal file
53
src/schemas/mutations/channels/closeChannel.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { closeChannel as lnCloseChannel } from "ln-service";
|
||||||
|
import { logger } from "../../../helpers/logger";
|
||||||
|
import { requestLimiter } from "../../../helpers/rateLimiter";
|
||||||
|
import { GraphQLBoolean, GraphQLString } from "graphql";
|
||||||
|
import { CloseChannelType } from "../../../schemaTypes/mutation.ts/channels/closeChannel";
|
||||||
|
|
||||||
|
interface CloseChannelProps {
|
||||||
|
transaction_id: string;
|
||||||
|
transaction_vout: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const closeChannel = {
|
||||||
|
type: CloseChannelType,
|
||||||
|
args: {
|
||||||
|
forceClose: {
|
||||||
|
type: GraphQLBoolean
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
|
transactionId: {
|
||||||
|
type: GraphQLString
|
||||||
|
},
|
||||||
|
transactionOutput: {
|
||||||
|
type: GraphQLString
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolve: async (root: any, params: any, context: any) => {
|
||||||
|
await requestLimiter(context.ip, params, "closeChannel", 1, "1s");
|
||||||
|
const { lnd } = context;
|
||||||
|
|
||||||
|
if (!params.id && !params.transactionId && !params.transactionOutput)
|
||||||
|
throw new Error(
|
||||||
|
"Id, transaction id or transaction output index are required"
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info: CloseChannelProps = await lnCloseChannel({
|
||||||
|
lnd: lnd,
|
||||||
|
id: params.id,
|
||||||
|
is_force_close: params.forceClose,
|
||||||
|
transaction_id: params.transactionId
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
transactionId: info.transaction_id,
|
||||||
|
transactionOutputIndex: info.transaction_vout
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error closing channel: %o", error);
|
||||||
|
throw new Error("Failed to close channel.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
7
src/schemas/mutations/channels/index.ts
Normal file
7
src/schemas/mutations/channels/index.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { closeChannel } from "./closeChannel";
|
||||||
|
import { openChannel } from "./openChannel";
|
||||||
|
|
||||||
|
export const channels = {
|
||||||
|
closeChannel,
|
||||||
|
openChannel
|
||||||
|
};
|
48
src/schemas/mutations/channels/openChannel.ts
Normal file
48
src/schemas/mutations/channels/openChannel.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { openChannel as lnOpenChannel } from "ln-service";
|
||||||
|
import { logger } from "../../../helpers/logger";
|
||||||
|
import { requestLimiter } from "../../../helpers/rateLimiter";
|
||||||
|
import { GraphQLBoolean, GraphQLString, GraphQLInt } from "graphql";
|
||||||
|
import { OpenChannelType } from "../../../schemaTypes/mutation.ts/channels/openChannel";
|
||||||
|
|
||||||
|
interface OpenChannelProps {
|
||||||
|
transaction_id: string;
|
||||||
|
transaction_vout: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const openChannel = {
|
||||||
|
type: OpenChannelType,
|
||||||
|
args: {
|
||||||
|
isPrivate: {
|
||||||
|
type: GraphQLBoolean
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
type: GraphQLInt
|
||||||
|
},
|
||||||
|
partnerPublicKey: {
|
||||||
|
type: GraphQLString
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolve: async (root: any, params: any, context: any) => {
|
||||||
|
await requestLimiter(context.ip, params, "openChannel", 1, "1s");
|
||||||
|
const { lnd } = context;
|
||||||
|
|
||||||
|
if (!params.amount || !params.partnerPublicKey)
|
||||||
|
throw new Error("Amount and partner public key are required");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info: OpenChannelProps = await lnOpenChannel({
|
||||||
|
lnd: lnd,
|
||||||
|
is_private: params.isPrivate,
|
||||||
|
local_tokens: params.amount,
|
||||||
|
partner_public_key: params.partnerPublicKey
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
transactionId: info.transaction_id,
|
||||||
|
transactionOutputIndex: info.transaction_vout
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error opening channel: %o", error);
|
||||||
|
throw new Error("Failed to open channel.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
4
src/schemas/mutations/index.ts
Normal file
4
src/schemas/mutations/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { channels } from "./channels";
|
||||||
|
import { invoices } from "./invoices";
|
||||||
|
|
||||||
|
export const mutation = { ...channels, ...invoices };
|
49
src/schemas/mutations/invoices/createInvoice.ts
Normal file
49
src/schemas/mutations/invoices/createInvoice.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { createInvoice as createInvoiceRequest } from "ln-service";
|
||||||
|
import { logger } from "../../../helpers/logger";
|
||||||
|
import { requestLimiter } from "../../../helpers/rateLimiter";
|
||||||
|
import { GraphQLNonNull, GraphQLInt } from "graphql";
|
||||||
|
import { InvoiceType } from "../../../schemaTypes/mutation.ts/invoice/createInvoice";
|
||||||
|
|
||||||
|
interface InvoiceProps {
|
||||||
|
chain_address: string;
|
||||||
|
created_at: string;
|
||||||
|
description: string;
|
||||||
|
id: string;
|
||||||
|
request: string;
|
||||||
|
secret: string;
|
||||||
|
tokens: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Allow more params
|
||||||
|
export const createInvoice = {
|
||||||
|
type: InvoiceType,
|
||||||
|
args: {
|
||||||
|
amount: {
|
||||||
|
type: new GraphQLNonNull(GraphQLInt)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolve: async (root: any, params: any, context: any) => {
|
||||||
|
await requestLimiter(context.ip, params, "createInvoice", 1, "1s");
|
||||||
|
const { lnd } = context;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const invoice: InvoiceProps = await createInvoiceRequest({
|
||||||
|
lnd: lnd,
|
||||||
|
tokens: params.amount
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
chainAddress: invoice.chain_address,
|
||||||
|
createdAt: invoice.created_at,
|
||||||
|
description: invoice.description,
|
||||||
|
id: invoice.id,
|
||||||
|
request: invoice.request,
|
||||||
|
secret: invoice.secret,
|
||||||
|
tokens: invoice.tokens
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error creating invoice: %o", error);
|
||||||
|
throw new Error("Failed to create invoice.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
9
src/schemas/mutations/invoices/index.ts
Normal file
9
src/schemas/mutations/invoices/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { parsePayment } from "./parsePayment";
|
||||||
|
import { pay } from "./pay";
|
||||||
|
import { createInvoice } from "./createInvoice";
|
||||||
|
|
||||||
|
export const invoices = {
|
||||||
|
parsePayment,
|
||||||
|
pay,
|
||||||
|
createInvoice
|
||||||
|
};
|
78
src/schemas/mutations/invoices/parsePayment.ts
Normal file
78
src/schemas/mutations/invoices/parsePayment.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import { parsePaymentRequest } from "ln-service";
|
||||||
|
import { logger } from "../../../helpers/logger";
|
||||||
|
import { requestLimiter } from "../../../helpers/rateLimiter";
|
||||||
|
import { GraphQLString, GraphQLNonNull } from "graphql";
|
||||||
|
import { ParsePaymentType } from "../../../schemaTypes/mutation.ts/invoice/parsePayment";
|
||||||
|
|
||||||
|
interface RouteProps {
|
||||||
|
base_fee_mtokens: string;
|
||||||
|
channel: string;
|
||||||
|
cltv_delta: number;
|
||||||
|
fee_rate: number;
|
||||||
|
public_key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestProps {
|
||||||
|
chain_addresses: string[];
|
||||||
|
cltv_delta: number;
|
||||||
|
created_at: string;
|
||||||
|
description: string;
|
||||||
|
description_hash: string;
|
||||||
|
destination: string;
|
||||||
|
expires_at: string;
|
||||||
|
id: string;
|
||||||
|
is_expired: string;
|
||||||
|
mtokens: string;
|
||||||
|
network: string;
|
||||||
|
routes: RouteProps[];
|
||||||
|
tokens: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parsePayment = {
|
||||||
|
type: ParsePaymentType,
|
||||||
|
args: {
|
||||||
|
request: {
|
||||||
|
type: new GraphQLNonNull(GraphQLString)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolve: async (root: any, params: any, context: any) => {
|
||||||
|
await requestLimiter(context.ip, params, "parsePayment", 1, "1s");
|
||||||
|
const { lnd } = context;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const request: RequestProps = await parsePaymentRequest({
|
||||||
|
lnd: lnd,
|
||||||
|
request: params.request
|
||||||
|
});
|
||||||
|
|
||||||
|
const routes = request.routes.map(route => {
|
||||||
|
return {
|
||||||
|
mTokenFee: route.base_fee_mtokens,
|
||||||
|
channel: route.channel,
|
||||||
|
cltvDelta: route.cltv_delta,
|
||||||
|
feeRate: route.fee_rate,
|
||||||
|
publicKey: route.public_key
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
chainAddresses: request.chain_addresses,
|
||||||
|
cltvDelta: request.cltv_delta,
|
||||||
|
createdAt: request.created_at,
|
||||||
|
description: request.description,
|
||||||
|
descriptionHash: request.description_hash,
|
||||||
|
destination: request.destination,
|
||||||
|
expiresAt: request.expires_at,
|
||||||
|
id: request.id,
|
||||||
|
isExpired: request.is_expired,
|
||||||
|
mTokens: request.mtokens,
|
||||||
|
network: request.network,
|
||||||
|
routes: routes,
|
||||||
|
tokens: request.tokens
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error decoding request: %o", error);
|
||||||
|
throw new Error("Failed to decode request.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
71
src/schemas/mutations/invoices/pay.ts
Normal file
71
src/schemas/mutations/invoices/pay.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { pay as payRequest } from "ln-service";
|
||||||
|
import { logger } from "../../../helpers/logger";
|
||||||
|
import { requestLimiter } from "../../../helpers/rateLimiter";
|
||||||
|
import { GraphQLString, GraphQLNonNull } from "graphql";
|
||||||
|
import { PayType } from "../../../schemaTypes/mutation.ts/invoice/pay";
|
||||||
|
|
||||||
|
interface HopProps {
|
||||||
|
channel: string;
|
||||||
|
channel_capacity: number;
|
||||||
|
fee_mtokens: string;
|
||||||
|
forward_mtokens: string;
|
||||||
|
timeout: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RequestProps {
|
||||||
|
fee: number;
|
||||||
|
fee_mtokens: string;
|
||||||
|
hops: HopProps[];
|
||||||
|
id: string;
|
||||||
|
is_confirmed: boolean;
|
||||||
|
is_outgoing: boolean;
|
||||||
|
mtokens: string;
|
||||||
|
secret: string;
|
||||||
|
tokens: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Allow path payments as well
|
||||||
|
export const pay = {
|
||||||
|
type: PayType,
|
||||||
|
args: {
|
||||||
|
request: {
|
||||||
|
type: new GraphQLNonNull(GraphQLString)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resolve: async (root: any, params: any, context: any) => {
|
||||||
|
await requestLimiter(context.ip, params, "payRequest", 1, "1s");
|
||||||
|
const { lnd } = context;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payment: RequestProps = await payRequest({
|
||||||
|
lnd: lnd,
|
||||||
|
request: params.request
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
fee: payment.fee,
|
||||||
|
feeMTokens: payment.fee_mtokens,
|
||||||
|
hops: hops,
|
||||||
|
id: payment.id,
|
||||||
|
isConfirmed: payment.is_confirmed,
|
||||||
|
isOutgoing: payment.is_outgoing,
|
||||||
|
mtokens: payment.mtokens,
|
||||||
|
secret: payment.secret,
|
||||||
|
tokens: payment.tokens
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error paying request: %o", error);
|
||||||
|
throw new Error("Failed to pay request.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -48,6 +48,7 @@ export const channels = {
|
||||||
const channels = channelList.channels.map((channel, index) => {
|
const channels = channelList.channels.map((channel, index) => {
|
||||||
return {
|
return {
|
||||||
capacity: channel.capacity,
|
capacity: channel.capacity,
|
||||||
|
id: channel.id,
|
||||||
isActive: channel.is_active,
|
isActive: channel.is_active,
|
||||||
isClosing: channel.is_closing,
|
isClosing: channel.is_closing,
|
||||||
isOpening: channel.is_opening,
|
isOpening: channel.is_opening,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { chainBalance } from "./chainBalance";
|
||||||
import { channelBalance } from "./channelBalance";
|
import { channelBalance } from "./channelBalance";
|
||||||
import { channels } from "./channels";
|
import { channels } from "./channels";
|
||||||
import { closedChannels } from "./closedChannels";
|
import { closedChannels } from "./closedChannels";
|
||||||
|
import { networkInfo } from "./networkInfo";
|
||||||
import { nodeInfo } from "./nodeInfo";
|
import { nodeInfo } from "./nodeInfo";
|
||||||
import { pendingChannels } from "./pendingChannels";
|
import { pendingChannels } from "./pendingChannels";
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ export const info = {
|
||||||
channelBalance,
|
channelBalance,
|
||||||
channels,
|
channels,
|
||||||
closedChannels,
|
closedChannels,
|
||||||
|
networkInfo,
|
||||||
nodeInfo,
|
nodeInfo,
|
||||||
pendingChannels
|
pendingChannels
|
||||||
};
|
};
|
||||||
|
|
43
src/schemas/query/info/networkInfo.ts
Normal file
43
src/schemas/query/info/networkInfo.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import { getNetworkInfo } from "ln-service";
|
||||||
|
import { logger } from "../../../helpers/logger";
|
||||||
|
import { requestLimiter } from "../../../helpers/rateLimiter";
|
||||||
|
import { NetworkInfoType } from "../../../schemaTypes/query/info/networkInfo";
|
||||||
|
|
||||||
|
interface NetworkInfoProps {
|
||||||
|
average_channel_size: number;
|
||||||
|
channel_count: number;
|
||||||
|
max_channel_size: number;
|
||||||
|
median_channel_size: number;
|
||||||
|
min_channel_size: number;
|
||||||
|
node_count: number;
|
||||||
|
not_recently_updated_policy_count: number;
|
||||||
|
total_capacity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const networkInfo = {
|
||||||
|
type: NetworkInfoType,
|
||||||
|
resolve: async (root: any, params: any, context: any) => {
|
||||||
|
await requestLimiter(context.ip, params, "networkInfo", 1, "1s");
|
||||||
|
const { lnd } = context;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const info: NetworkInfoProps = await getNetworkInfo({
|
||||||
|
lnd: lnd
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
averageChannelSize: info.average_channel_size,
|
||||||
|
channelCount: info.channel_count,
|
||||||
|
maxChannelSize: info.max_channel_size,
|
||||||
|
medianChannelSize: info.median_channel_size,
|
||||||
|
minChannelSize: info.min_channel_size,
|
||||||
|
nodeCount: info.node_count,
|
||||||
|
notRecentlyUpdatedPolicyCount: info.not_recently_updated_policy_count,
|
||||||
|
totalCapacity: info.total_capacity
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error getting network info: %o", error);
|
||||||
|
throw new Error("Failed to get network info.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue