dist
This commit is contained in:
parent
940a728724
commit
6d5be18915
2
dist/src/cli.d.ts
vendored
Normal file
2
dist/src/cli.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env ts-node
|
||||
export {};
|
||||
230
dist/src/cli.js
vendored
Normal file
230
dist/src/cli.js
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
#!/usr/bin/env ts-node
|
||||
/* eslint-disable @typescript-eslint/no-empty-function */
|
||||
/* eslint-disable @typescript-eslint/no-shadow */
|
||||
import yargs from "yargs/yargs";
|
||||
import { hideBin } from "yargs/helpers";
|
||||
import { NitroRpcClient } from "./rpc-client";
|
||||
import { compactJson, getLocalRPCUrl, logOutChannelUpdates } from "./utils";
|
||||
yargs(hideBin(process.argv))
|
||||
.scriptName("nitro-rpc-client")
|
||||
.option({
|
||||
p: { alias: "port", default: 4005, type: "number" },
|
||||
n: {
|
||||
alias: "printnotifications",
|
||||
default: false,
|
||||
type: "boolean",
|
||||
description: "Whether channel notifications are printed to the console",
|
||||
},
|
||||
})
|
||||
.command("version", "Get the version of the Nitro RPC server", async () => { }, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
const version = await rpcClient.GetVersion();
|
||||
console.log(version);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("address", "Get the address of the Nitro RPC server", async () => { }, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
const address = await rpcClient.GetAddress();
|
||||
console.log(address);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("get-all-ledger-channels", "Get all ledger channels", async () => { }, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
const ledgers = await rpcClient.GetAllLedgerChannels();
|
||||
for (const ledger of ledgers) {
|
||||
console.log(`${compactJson(ledger)}`);
|
||||
}
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("get-payment-channels-by-ledger <ledgerId>", "Gets any payment channels funded by the given ledger", (yargsBuilder) => {
|
||||
return yargsBuilder.positional("ledgerId", {
|
||||
describe: "The id of the ledger channel to defund",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
const paymentChans = await rpcClient.GetPaymentChannelsByLedger(yargs.ledgerId);
|
||||
for (const p of paymentChans) {
|
||||
console.log(`${compactJson(p)}`);
|
||||
}
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("direct-fund <counterparty>", "Creates a directly funded ledger channel", (yargsBuilder) => {
|
||||
return yargsBuilder
|
||||
.positional("counterparty", {
|
||||
describe: "The counterparty's address",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
})
|
||||
.option("amount", {
|
||||
describe: "The amount to fund the channel with",
|
||||
type: "number",
|
||||
default: 1_000_000,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
if (yargs.n)
|
||||
logOutChannelUpdates(rpcClient);
|
||||
const dfObjective = await rpcClient.CreateLedgerChannel(yargs.counterparty, yargs.amount);
|
||||
const { Id, ChannelId } = dfObjective;
|
||||
console.log(`Objective started ${Id}`);
|
||||
await rpcClient.WaitForLedgerChannelStatus(ChannelId, "Open");
|
||||
console.log(`Channel Open ${ChannelId}`);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("direct-defund <channelId>", "Defunds a directly funded ledger channel", (yargsBuilder) => {
|
||||
return yargsBuilder.positional("channelId", {
|
||||
describe: "The id of the ledger channel to defund",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
if (yargs.n)
|
||||
logOutChannelUpdates(rpcClient);
|
||||
const id = await rpcClient.CloseLedgerChannel(yargs.channelId);
|
||||
console.log(`Objective started ${id}`);
|
||||
await rpcClient.WaitForPaymentChannelStatus(yargs.channelId, "Complete");
|
||||
console.log(`Channel Complete ${yargs.channelId}`);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("virtual-fund <counterparty> [intermediaries...]", "Creates a virtually funded payment channel", (yargsBuilder) => {
|
||||
return yargsBuilder
|
||||
.positional("counterparty", {
|
||||
describe: "The counterparty's address",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
})
|
||||
.array("intermediaries")
|
||||
.option("amount", {
|
||||
describe: "The amount to fund the channel with",
|
||||
type: "number",
|
||||
default: 1000,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
if (yargs.n)
|
||||
logOutChannelUpdates(rpcClient);
|
||||
// Parse all intermediary args to strings
|
||||
const intermediaries = yargs.intermediaries?.map((intermediary) => {
|
||||
if (typeof intermediary === "string") {
|
||||
return intermediary;
|
||||
}
|
||||
return intermediary.toString(16);
|
||||
}) ?? [];
|
||||
const vfObjective = await rpcClient.CreatePaymentChannel(yargs.counterparty, intermediaries, yargs.amount);
|
||||
const { ChannelId, Id } = vfObjective;
|
||||
console.log(`Objective started ${Id}`);
|
||||
await rpcClient.WaitForPaymentChannelStatus(ChannelId, "Open");
|
||||
console.log(`Channel Open ${ChannelId}`);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("virtual-defund <channelId>", "Defunds a virtually funded payment channel", (yargsBuilder) => {
|
||||
return yargsBuilder.positional("channelId", {
|
||||
describe: "The id of the payment channel to defund",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
if (yargs.n)
|
||||
logOutChannelUpdates(rpcClient);
|
||||
const id = await rpcClient.ClosePaymentChannel(yargs.channelId);
|
||||
console.log(`Objective started ${id}`);
|
||||
await rpcClient.WaitForPaymentChannelStatus(yargs.channelId, "Complete");
|
||||
console.log(`Channel complete ${yargs.channelId}`);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("get-ledger-channel <channelId>", "Gets information about a ledger channel", (yargsBuilder) => {
|
||||
return yargsBuilder.positional("channelId", {
|
||||
describe: "The channel ID of the ledger channel",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
const ledgerInfo = await rpcClient.GetLedgerChannel(yargs.channelId);
|
||||
console.log(ledgerInfo);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("get-payment-channel <channelId>", "Gets information about a payment channel", (yargsBuilder) => {
|
||||
return yargsBuilder.positional("channelId", {
|
||||
describe: "The channel ID of the payment channel",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
const paymentChannelInfo = await rpcClient.GetPaymentChannel(yargs.channelId);
|
||||
console.log(paymentChannelInfo);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("pay <channelId> <amount>", "Sends a payment on the given channel", (yargsBuilder) => {
|
||||
return yargsBuilder
|
||||
.positional("channelId", {
|
||||
describe: "The channel ID of the payment channel",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
})
|
||||
.positional("amount", {
|
||||
describe: "The amount to pay",
|
||||
type: "number",
|
||||
demandOption: true,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
if (yargs.n)
|
||||
logOutChannelUpdates(rpcClient);
|
||||
const paymentChannelInfo = await rpcClient.Pay(yargs.channelId, yargs.amount);
|
||||
console.log(paymentChannelInfo);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.command("create-voucher <channelId> <amount>", "Create a payment on the given channel", (yargsBuilder) => {
|
||||
return yargsBuilder
|
||||
.positional("channelId", {
|
||||
describe: "The channel ID of the payment channel",
|
||||
type: "string",
|
||||
demandOption: true,
|
||||
})
|
||||
.positional("amount", {
|
||||
describe: "The amount to pay",
|
||||
type: "number",
|
||||
demandOption: true,
|
||||
});
|
||||
}, async (yargs) => {
|
||||
const rpcPort = yargs.p;
|
||||
const rpcClient = await NitroRpcClient.CreateHttpNitroClient(getLocalRPCUrl(rpcPort));
|
||||
if (yargs.n)
|
||||
logOutChannelUpdates(rpcClient);
|
||||
const voucher = await rpcClient.CreateVoucher(yargs.channelId, yargs.amount);
|
||||
console.log(voucher);
|
||||
await rpcClient.Close();
|
||||
process.exit(0);
|
||||
})
|
||||
.demandCommand(1, "You need at least one command before moving on")
|
||||
.parserConfiguration({ "parse-numbers": false })
|
||||
.strict()
|
||||
.parse();
|
||||
1
dist/src/index.d.ts
vendored
Normal file
1
dist/src/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export { NitroRpcClient } from "./rpc-client";
|
||||
1
dist/src/index.js
vendored
Normal file
1
dist/src/index.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
export { NitroRpcClient } from "./rpc-client";
|
||||
125
dist/src/interface.d.ts
vendored
Normal file
125
dist/src/interface.d.ts
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
import { ChannelStatus, LedgerChannelInfo, ObjectiveResponse, PaymentChannelInfo, PaymentPayload, ReceiveVoucherResult, Voucher } from "./types";
|
||||
interface ledgerChannelApi {
|
||||
/**
|
||||
* CreateLedgerChannel creates a directly funded ledger channel with the counterparty.
|
||||
*
|
||||
* @param counterParty - The counterparty to create the channel with
|
||||
* @returns A promise that resolves to an objective response, containing the ID of the objective and the channel id.
|
||||
*/
|
||||
CreateLedgerChannel(counterParty: string, amount: number): Promise<ObjectiveResponse>;
|
||||
/**
|
||||
* CloseLedgerChannel defunds a directly funded ledger channel.
|
||||
*
|
||||
* @param channelId - The ID of the channel to defund
|
||||
* @returns The ID of the objective that was created
|
||||
*/
|
||||
CloseLedgerChannel(channelId: string): Promise<string>;
|
||||
/**
|
||||
* GetLedgerChannel queries the RPC server for a payment channel.
|
||||
*
|
||||
* @param channelId - The ID of the channel to query for
|
||||
* @returns A `LedgerChannelInfo` object containing the channel's information
|
||||
*/
|
||||
GetLedgerChannel(channelId: string): Promise<LedgerChannelInfo>;
|
||||
/**
|
||||
* GetAllLedgerChannels queries the RPC server for all ledger channels.
|
||||
* @returns A `LedgerChannelInfo` object containing the channel's information for each ledger channel
|
||||
*/
|
||||
GetAllLedgerChannels(): Promise<LedgerChannelInfo[]>;
|
||||
}
|
||||
interface paymentChannelApi {
|
||||
/**
|
||||
* CreatePaymentChannel creates a virtually funded payment channel with the counterparty, using the given intermediaries.
|
||||
*
|
||||
* @param counterParty - The counterparty to create the channel with
|
||||
* @param intermediaries - The intermerdiaries to use
|
||||
* @returns A promise that resolves to an objective response, containing the ID of the objective and the channel id.
|
||||
*/
|
||||
CreatePaymentChannel(counterParty: string, intermediaries: string[], amount: number): Promise<ObjectiveResponse>;
|
||||
/**
|
||||
* ClosePaymentChannel defunds a virtually funded payment channel.
|
||||
*
|
||||
* @param channelId - The ID of the channel to defund
|
||||
* @returns The ID of the objective that was created
|
||||
*/
|
||||
ClosePaymentChannel(channelId: string): Promise<string>;
|
||||
/**
|
||||
* GetPaymentChannel queries the RPC server for a payment channel.
|
||||
*
|
||||
* @param channelId - The ID of the channel to query for
|
||||
* @returns A `PaymentChannelInfo` object containing the channel's information
|
||||
*/
|
||||
GetPaymentChannel(channelId: string): Promise<PaymentChannelInfo>;
|
||||
/**
|
||||
* GetPaymentChannelsByLedger queries the RPC server for any payment channels that are actively funded by the given ledger.
|
||||
*
|
||||
* @param ledgerId - The ID of the ledger to find payment channels for
|
||||
* @returns A `PaymentChannelInfo` object containing the channel's information for each payment channel
|
||||
*/
|
||||
GetPaymentChannelsByLedger(ledgerId: string): Promise<PaymentChannelInfo[]>;
|
||||
}
|
||||
interface paymentApi {
|
||||
/**
|
||||
* Creates a payment voucher for the given channel and amount.
|
||||
* The voucher does not get sent to the other party automatically.
|
||||
* @param channelId The payment channel to use for the voucher
|
||||
* @param amount The amount for the voucher
|
||||
* @returns A signed voucher
|
||||
*/
|
||||
CreateVoucher(channelId: string, amount: number): Promise<Voucher>;
|
||||
/**
|
||||
* Adds a voucher to the go-nitro node that was received from the other party to the channel.
|
||||
* @param voucher The voucher to add
|
||||
* @returns The total amount of the channel and the delta of the voucher
|
||||
*/
|
||||
ReceiveVoucher(voucher: Voucher): Promise<ReceiveVoucherResult>;
|
||||
/**
|
||||
* Pay sends a payment on a virtual payment chanel.
|
||||
*
|
||||
* @param channelId - The ID of the payment channel to use
|
||||
* @param amount - The amount to pay
|
||||
*/
|
||||
Pay(channelId: string, amount: number): Promise<PaymentPayload>;
|
||||
}
|
||||
interface syncAPI {
|
||||
/**
|
||||
* WaitForLedgerChannelStatus blocks until the ledger channel with the given ID to have the given status.
|
||||
*
|
||||
* @param objectiveId - The channel id to wait for
|
||||
* @param status - The channel id to wait for (e.g. Ready or Closing)
|
||||
*/
|
||||
WaitForLedgerChannelStatus(objectiveId: string, status: ChannelStatus): Promise<void>;
|
||||
/**
|
||||
* WaitForPaymentChannelStatus blocks until the payment channel with the given ID to have the given status.
|
||||
*
|
||||
* @param objectiveId - The channel id to wait for
|
||||
* @param status - The channel id to wait for (e.g. Ready or Closing)
|
||||
*/
|
||||
WaitForPaymentChannelStatus(objectiveId: string, status: ChannelStatus): Promise<void>;
|
||||
/**
|
||||
* PaymentChannelUpdated attaches a callback which is triggered when the channel with supplied ID is updated.
|
||||
* Returns a cleanup function which can be used to remove the subscription.
|
||||
*
|
||||
* @param objectiveId - The id objective to wait for
|
||||
*/
|
||||
onPaymentChannelUpdated(channelId: string, callback: (info: PaymentChannelInfo) => void): () => void;
|
||||
}
|
||||
export interface RpcClientApi extends ledgerChannelApi, paymentChannelApi, paymentApi, syncAPI {
|
||||
/**
|
||||
* GetVersion queries the API server for it's version.
|
||||
*
|
||||
* @returns The version of the RPC server
|
||||
*/
|
||||
GetVersion(): Promise<string>;
|
||||
/**
|
||||
* GetAddress queries the RPC server for it's state channel address.
|
||||
*
|
||||
* @returns The address of the wallet connected to the RPC server
|
||||
*/
|
||||
GetAddress(): Promise<string>;
|
||||
/**
|
||||
* Close closes the RPC client and stops listening for notifications.
|
||||
*/
|
||||
Close(): Promise<void>;
|
||||
}
|
||||
export {};
|
||||
1
dist/src/interface.js
vendored
Normal file
1
dist/src/interface.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
41
dist/src/rpc-client.d.ts
vendored
Normal file
41
dist/src/rpc-client.d.ts
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
import { LedgerChannelInfo, PaymentChannelInfo, PaymentPayload, ObjectiveResponse, Voucher, ReceiveVoucherResult, ChannelStatus } from "./types";
|
||||
import { RpcClientApi } from "./interface";
|
||||
export declare class NitroRpcClient implements RpcClientApi {
|
||||
private transport;
|
||||
private myAddress;
|
||||
private authToken;
|
||||
get Notifications(): import("eventemitter3").EventEmitter<"objective_completed" | "payment_channel_updated" | "ledger_channel_updated", {
|
||||
payload: string;
|
||||
} | {
|
||||
payload: PaymentChannelInfo;
|
||||
} | {
|
||||
payload: LedgerChannelInfo;
|
||||
}>;
|
||||
CreateVoucher(channelId: string, amount: number): Promise<Voucher>;
|
||||
ReceiveVoucher(voucher: Voucher): Promise<ReceiveVoucherResult>;
|
||||
WaitForLedgerChannelStatus(channelId: string, status: ChannelStatus): Promise<void>;
|
||||
WaitForPaymentChannelStatus(channelId: string, status: ChannelStatus): Promise<void>;
|
||||
onPaymentChannelUpdated(channelId: string, callback: (info: PaymentChannelInfo) => void): () => void;
|
||||
CreateLedgerChannel(counterParty: string, amount: number): Promise<ObjectiveResponse>;
|
||||
CreatePaymentChannel(counterParty: string, intermediaries: string[], amount: number): Promise<ObjectiveResponse>;
|
||||
Pay(channelId: string, amount: number): Promise<PaymentPayload>;
|
||||
CloseLedgerChannel(channelId: string): Promise<string>;
|
||||
ClosePaymentChannel(channelId: string): Promise<string>;
|
||||
GetVersion(): Promise<string>;
|
||||
GetAddress(): Promise<string>;
|
||||
GetLedgerChannel(channelId: string): Promise<LedgerChannelInfo>;
|
||||
GetAllLedgerChannels(): Promise<LedgerChannelInfo[]>;
|
||||
GetPaymentChannel(channelId: string): Promise<PaymentChannelInfo>;
|
||||
GetPaymentChannelsByLedger(ledgerId: string): Promise<PaymentChannelInfo[]>;
|
||||
private getAuthToken;
|
||||
private sendRequest;
|
||||
Close(): Promise<void>;
|
||||
private constructor();
|
||||
/**
|
||||
* Creates an RPC client that uses HTTP/WS as the transport.
|
||||
*
|
||||
* @param url - The URL of the HTTP/WS server
|
||||
* @returns A NitroRpcClient that uses WS as the transport
|
||||
*/
|
||||
static CreateHttpNitroClient(url: string): Promise<NitroRpcClient>;
|
||||
}
|
||||
160
dist/src/rpc-client.js
vendored
Normal file
160
dist/src/rpc-client.js
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
import { createOutcome, generateRequest } from "./utils";
|
||||
import { HttpTransport } from "./transport/http";
|
||||
import { getAndValidateResult } from "./serde";
|
||||
export class NitroRpcClient {
|
||||
transport;
|
||||
// We fetch the address from the RPC server on first use
|
||||
myAddress;
|
||||
authToken;
|
||||
get Notifications() {
|
||||
return this.transport.Notifications;
|
||||
}
|
||||
async CreateVoucher(channelId, amount) {
|
||||
const payload = {
|
||||
Amount: amount,
|
||||
Channel: channelId,
|
||||
};
|
||||
const request = generateRequest("create_voucher", payload, this.authToken || "");
|
||||
const res = await this.transport.sendRequest(request);
|
||||
return getAndValidateResult(res, "create_voucher");
|
||||
}
|
||||
async ReceiveVoucher(voucher) {
|
||||
const request = generateRequest("receive_voucher", voucher, this.authToken || "");
|
||||
const res = await this.transport.sendRequest(request);
|
||||
return getAndValidateResult(res, "receive_voucher");
|
||||
}
|
||||
async WaitForLedgerChannelStatus(channelId, status) {
|
||||
const promise = new Promise((resolve) => {
|
||||
this.transport.Notifications.on("ledger_channel_updated", (payload) => {
|
||||
if (payload.ID === channelId) {
|
||||
this.GetLedgerChannel(channelId).then((l) => {
|
||||
if (l.Status == status)
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
const ledger = await this.GetLedgerChannel(channelId);
|
||||
if (ledger.Status == status)
|
||||
return;
|
||||
return promise;
|
||||
}
|
||||
async WaitForPaymentChannelStatus(channelId, status) {
|
||||
const promise = new Promise((resolve) => {
|
||||
this.transport.Notifications.on("payment_channel_updated", (payload) => {
|
||||
if (payload.ID === channelId) {
|
||||
this.GetPaymentChannel(channelId).then((l) => {
|
||||
if (l.Status == status)
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
const channel = await this.GetPaymentChannel(channelId);
|
||||
if (channel.Status == status)
|
||||
return;
|
||||
return promise;
|
||||
}
|
||||
onPaymentChannelUpdated(channelId, callback) {
|
||||
const wrapperFn = (info) => {
|
||||
if (info.ID.toLowerCase() == channelId.toLowerCase()) {
|
||||
callback(info);
|
||||
}
|
||||
};
|
||||
this.transport.Notifications.on("payment_channel_updated", wrapperFn);
|
||||
return () => {
|
||||
this.transport.Notifications.off("payment_channel_updated", wrapperFn);
|
||||
};
|
||||
}
|
||||
async CreateLedgerChannel(counterParty, amount) {
|
||||
const asset = `0x${"00".repeat(20)}`;
|
||||
const payload = {
|
||||
CounterParty: counterParty,
|
||||
ChallengeDuration: 0,
|
||||
Outcome: createOutcome(asset, await this.GetAddress(), counterParty, amount),
|
||||
AppDefinition: asset,
|
||||
AppData: "0x00",
|
||||
Nonce: Date.now(),
|
||||
};
|
||||
return this.sendRequest("create_ledger_channel", payload);
|
||||
}
|
||||
async CreatePaymentChannel(counterParty, intermediaries, amount) {
|
||||
const asset = `0x${"00".repeat(20)}`;
|
||||
const payload = {
|
||||
CounterParty: counterParty,
|
||||
Intermediaries: intermediaries,
|
||||
ChallengeDuration: 0,
|
||||
Outcome: createOutcome(asset, await this.GetAddress(), counterParty, amount),
|
||||
AppDefinition: asset,
|
||||
Nonce: Date.now(),
|
||||
};
|
||||
return this.sendRequest("create_payment_channel", payload);
|
||||
}
|
||||
async Pay(channelId, amount) {
|
||||
const payload = {
|
||||
Amount: amount,
|
||||
Channel: channelId,
|
||||
};
|
||||
const request = generateRequest("pay", payload, this.authToken || "");
|
||||
const res = await this.transport.sendRequest(request);
|
||||
return getAndValidateResult(res, "pay");
|
||||
}
|
||||
async CloseLedgerChannel(channelId) {
|
||||
const payload = { ChannelId: channelId };
|
||||
return this.sendRequest("close_ledger_channel", payload);
|
||||
}
|
||||
async ClosePaymentChannel(channelId) {
|
||||
const payload = { ChannelId: channelId };
|
||||
return this.sendRequest("close_payment_channel", payload);
|
||||
}
|
||||
async GetVersion() {
|
||||
return this.sendRequest("version", {});
|
||||
}
|
||||
async GetAddress() {
|
||||
if (this.myAddress) {
|
||||
return this.myAddress;
|
||||
}
|
||||
this.myAddress = await this.sendRequest("get_address", {});
|
||||
return this.myAddress;
|
||||
}
|
||||
async GetLedgerChannel(channelId) {
|
||||
return this.sendRequest("get_ledger_channel", { Id: channelId });
|
||||
}
|
||||
async GetAllLedgerChannels() {
|
||||
return this.sendRequest("get_all_ledger_channels", {});
|
||||
}
|
||||
async GetPaymentChannel(channelId) {
|
||||
return this.sendRequest("get_payment_channel", { Id: channelId });
|
||||
}
|
||||
async GetPaymentChannelsByLedger(ledgerId) {
|
||||
return this.sendRequest("get_payment_channels_by_ledger", {
|
||||
LedgerId: ledgerId,
|
||||
});
|
||||
}
|
||||
async getAuthToken() {
|
||||
return this.sendRequest("get_auth_token", {});
|
||||
}
|
||||
async sendRequest(method, payload) {
|
||||
const request = generateRequest(method, payload, this.authToken || "");
|
||||
const res = await this.transport.sendRequest(request);
|
||||
return getAndValidateResult(res, method);
|
||||
}
|
||||
async Close() {
|
||||
return this.transport.Close();
|
||||
}
|
||||
constructor(transport) {
|
||||
this.transport = transport;
|
||||
}
|
||||
/**
|
||||
* Creates an RPC client that uses HTTP/WS as the transport.
|
||||
*
|
||||
* @param url - The URL of the HTTP/WS server
|
||||
* @returns A NitroRpcClient that uses WS as the transport
|
||||
*/
|
||||
static async CreateHttpNitroClient(url) {
|
||||
const transport = await HttpTransport.createTransport(url);
|
||||
const rpcClient = new NitroRpcClient(transport);
|
||||
rpcClient.authToken = await rpcClient.getAuthToken();
|
||||
return rpcClient;
|
||||
}
|
||||
}
|
||||
9
dist/src/serde.d.ts
vendored
Normal file
9
dist/src/serde.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
import { RPCNotification, RPCRequestAndResponses, RequestMethod } from "./types";
|
||||
/**
|
||||
* Validates that the response is a valid JSON RPC response with a valid result
|
||||
* @param response - JSON RPC response
|
||||
* @param method - JSON RPC method
|
||||
* @returns The validated result of the JSON RPC response
|
||||
*/
|
||||
export declare function getAndValidateResult<T extends RequestMethod>(response: unknown, method: T): RPCRequestAndResponses[T][1]["result"];
|
||||
export declare function getAndValidateNotification<T extends RPCNotification["method"]>(data: unknown, method: T): RPCNotification["params"]["payload"];
|
||||
204
dist/src/serde.js
vendored
Normal file
204
dist/src/serde.js
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
import Ajv from "ajv/dist/jtd";
|
||||
const ajv = new Ajv();
|
||||
const jsonRpcSchema = {
|
||||
properties: {
|
||||
jsonrpc: { type: "string" },
|
||||
id: { type: "uint32" },
|
||||
},
|
||||
optionalProperties: {
|
||||
result: {
|
||||
nullable: true,
|
||||
},
|
||||
error: {
|
||||
properties: {
|
||||
code: { type: "int32" },
|
||||
message: { type: "string" },
|
||||
},
|
||||
additionalProperties: true,
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const objectiveSchema = {
|
||||
properties: {
|
||||
Id: { type: "string" },
|
||||
ChannelId: { type: "string" },
|
||||
},
|
||||
};
|
||||
const stringSchema = { type: "string" };
|
||||
const ledgerChannelSchema = {
|
||||
properties: {
|
||||
ID: { type: "string" },
|
||||
Status: { type: "string" },
|
||||
Balance: {
|
||||
properties: {
|
||||
AssetAddress: { type: "string" },
|
||||
Them: { type: "string" },
|
||||
Me: { type: "string" },
|
||||
MyBalance: { type: "string" },
|
||||
TheirBalance: { type: "string" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const paymentChannelSchema = {
|
||||
properties: {
|
||||
ID: { type: "string" },
|
||||
Status: { type: "string" },
|
||||
Balance: {
|
||||
properties: {
|
||||
AssetAddress: { type: "string" },
|
||||
Payee: { type: "string" },
|
||||
Payer: { type: "string" },
|
||||
PaidSoFar: { type: "string" },
|
||||
RemainingFunds: { type: "string" },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const ledgerChannelsSchema = {
|
||||
elements: {
|
||||
...ledgerChannelSchema,
|
||||
},
|
||||
};
|
||||
const paymentChannelsSchema = {
|
||||
elements: {
|
||||
...paymentChannelSchema,
|
||||
},
|
||||
};
|
||||
const paymentSchema = {
|
||||
properties: {
|
||||
Amount: { type: "uint32" },
|
||||
Channel: { type: "string" },
|
||||
},
|
||||
};
|
||||
const voucherSchema = {
|
||||
properties: {
|
||||
ChannelId: { type: "string" },
|
||||
Amount: { type: "uint32" },
|
||||
Signature: {
|
||||
type: "string",
|
||||
},
|
||||
},
|
||||
};
|
||||
const receiveVoucherSchema = {
|
||||
properties: {
|
||||
Total: { type: "string" },
|
||||
Delta: { type: "string" },
|
||||
},
|
||||
};
|
||||
/**
|
||||
* Validates that the response is a valid JSON RPC response with a valid result
|
||||
* @param response - JSON RPC response
|
||||
* @param method - JSON RPC method
|
||||
* @returns The validated result of the JSON RPC response
|
||||
*/
|
||||
export function getAndValidateResult(response, method) {
|
||||
const { result, error } = getJsonRpcResult(response);
|
||||
if (error) {
|
||||
throw new Error("jsonrpc response: " + error.message);
|
||||
}
|
||||
switch (method) {
|
||||
case "create_ledger_channel":
|
||||
case "create_payment_channel":
|
||||
return validateAndConvertResult(objectiveSchema, result, (result) => result);
|
||||
case "get_auth_token":
|
||||
case "close_ledger_channel":
|
||||
case "version":
|
||||
case "get_address":
|
||||
case "close_payment_channel":
|
||||
return validateAndConvertResult(stringSchema, result, (result) => result);
|
||||
case "get_ledger_channel":
|
||||
return validateAndConvertResult(ledgerChannelSchema, result, convertToInternalLedgerChannelType);
|
||||
case "get_all_ledger_channels":
|
||||
return validateAndConvertResult(ledgerChannelsSchema, result, convertToInternalLedgerChannelsType);
|
||||
case "get_payment_channel":
|
||||
return validateAndConvertResult(paymentChannelSchema, result, convertToInternalPaymentChannelType);
|
||||
case "get_payment_channels_by_ledger":
|
||||
return validateAndConvertResult(paymentChannelsSchema, result, convertToInternalPaymentChannelsType);
|
||||
case "pay":
|
||||
return validateAndConvertResult(paymentSchema, result, (result) => result);
|
||||
case "receive_voucher":
|
||||
return validateAndConvertResult(receiveVoucherSchema, result, (result) => ({
|
||||
Total: BigInt(result.Total),
|
||||
Delta: BigInt(result.Delta),
|
||||
}));
|
||||
case "create_voucher":
|
||||
return validateAndConvertResult(voucherSchema, result, (result) => {
|
||||
return {
|
||||
...result,
|
||||
};
|
||||
});
|
||||
default:
|
||||
throw new Error(`Unknown method: ${method}`);
|
||||
}
|
||||
}
|
||||
export function getAndValidateNotification(data, method) {
|
||||
switch (method) {
|
||||
case "payment_channel_updated":
|
||||
return convertToInternalPaymentChannelType(data);
|
||||
case "ledger_channel_updated":
|
||||
return convertToInternalPaymentChannelType(data);
|
||||
case "objective_completed":
|
||||
return data;
|
||||
default:
|
||||
throw new Error(`Unknown method: ${method}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validates that the response is a valid JSON RPC response and pulls out the result
|
||||
* @param response - JSON RPC response
|
||||
* @returns The result of the response
|
||||
*/
|
||||
function getJsonRpcResult(response) {
|
||||
const validate = ajv.compile(jsonRpcSchema);
|
||||
if (validate(response)) {
|
||||
return response;
|
||||
}
|
||||
throw new Error(`Invalid json rpc response: ${JSON.stringify(validate.errors)}. The response is ${JSON.stringify(response)}`);
|
||||
}
|
||||
/**
|
||||
* validateAndConvertResult validates that the result object conforms to the schema and converts it to the internal type
|
||||
*
|
||||
* @param schema - JSON Type Definition
|
||||
* @param result - Object to validate
|
||||
* @param converstionFn - Function to convert the valiated object to internal type
|
||||
* @returns A validated object of internal type
|
||||
*/
|
||||
function validateAndConvertResult(schema, result, converstionFn) {
|
||||
const validate = ajv.compile(schema);
|
||||
if (validate(result)) {
|
||||
return converstionFn(result);
|
||||
}
|
||||
throw new Error(`Error parsing json rpc result: ${JSON.stringify(validate.errors)}. The result is ${JSON.stringify(result)}`);
|
||||
}
|
||||
function convertToInternalLedgerChannelType(result) {
|
||||
// todo: validate channel status
|
||||
return {
|
||||
...result,
|
||||
Status: result.Status,
|
||||
Balance: {
|
||||
...result.Balance,
|
||||
TheirBalance: BigInt(result.Balance.TheirBalance),
|
||||
MyBalance: BigInt(result.Balance.MyBalance),
|
||||
},
|
||||
};
|
||||
}
|
||||
function convertToInternalLedgerChannelsType(result) {
|
||||
return result.map((lc) => convertToInternalLedgerChannelType(lc));
|
||||
}
|
||||
function convertToInternalPaymentChannelType(result) {
|
||||
// todo: validate channel status
|
||||
return {
|
||||
...result,
|
||||
Status: result.Status,
|
||||
Balance: {
|
||||
...result.Balance,
|
||||
PaidSoFar: BigInt(result.Balance.PaidSoFar ?? 0),
|
||||
RemainingFunds: BigInt(result.Balance.RemainingFunds ?? 0),
|
||||
},
|
||||
};
|
||||
}
|
||||
function convertToInternalPaymentChannelsType(result) {
|
||||
return result.map((pc) => convertToInternalPaymentChannelType(pc));
|
||||
}
|
||||
1
dist/src/serde.test.d.ts
vendored
Normal file
1
dist/src/serde.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
79
dist/src/serde.test.js
vendored
Normal file
79
dist/src/serde.test.js
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
import { getAndValidateResult } from "./serde";
|
||||
describe("get_address", () => {
|
||||
it("success: validate response string", () => {
|
||||
const getAddressResponse = {
|
||||
jsonrpc: "2.0",
|
||||
id: 168513765,
|
||||
result: "0x111A00868581f73AB42FEEF67D235Ca09ca1E8db",
|
||||
};
|
||||
const validatedResponse = getAndValidateResult(getAddressResponse, "get_address");
|
||||
expect(validatedResponse).toEqual(getAddressResponse.result);
|
||||
});
|
||||
});
|
||||
describe("get_ledger_channel", () => {
|
||||
it("success: validate response object", () => {
|
||||
const getLedgerChannelResponse = {
|
||||
jsonrpc: "2.0",
|
||||
id: 168513765,
|
||||
result: {
|
||||
ID: "0x586d127530f69177d790bb940eae132922e7648c29264648af5375de2c19e270",
|
||||
Status: "Open",
|
||||
Balance: {
|
||||
AssetAddress: "0x0000000000000000000000000000000000000000",
|
||||
Them: "0x111a00868581f73ab42feef67d235ca09ca1e8db",
|
||||
Me: "0xaaa6628ec44a8a742987ef3a114ddfe2d4f7adce",
|
||||
TheirBalance: "0xf368a",
|
||||
MyBalance: "0xf3686",
|
||||
},
|
||||
},
|
||||
};
|
||||
const validatedGetLedgerChannelResponse = {
|
||||
ID: "0x586d127530f69177d790bb940eae132922e7648c29264648af5375de2c19e270",
|
||||
Status: "Open",
|
||||
Balance: {
|
||||
AssetAddress: "0x0000000000000000000000000000000000000000",
|
||||
Them: "0x111a00868581f73ab42feef67d235ca09ca1e8db",
|
||||
Me: "0xaaa6628ec44a8a742987ef3a114ddfe2d4f7adce",
|
||||
TheirBalance: 997002n,
|
||||
MyBalance: 996998n,
|
||||
},
|
||||
};
|
||||
const validatedResponse = getAndValidateResult(getLedgerChannelResponse, "get_ledger_channel");
|
||||
expect(validatedResponse).toEqual(validatedGetLedgerChannelResponse);
|
||||
});
|
||||
});
|
||||
describe("create_ledger_channel", () => {
|
||||
it("error", () => {
|
||||
const failedCreateLedgerResponse = {
|
||||
jsonrpc: "2.0",
|
||||
id: 168513765,
|
||||
error: {
|
||||
code: -32603,
|
||||
message: "Internal Server Error",
|
||||
},
|
||||
};
|
||||
try {
|
||||
getAndValidateResult(failedCreateLedgerResponse, "create_ledger_channel");
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof Error) {
|
||||
expect(err.message).toEqual("jsonrpc response: Internal Server Error");
|
||||
}
|
||||
else {
|
||||
expect(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
it("success: validate response object", () => {
|
||||
const successCreateLedgerResponse = {
|
||||
jsonrpc: "2.0",
|
||||
id: 995772692,
|
||||
result: {
|
||||
Id: "123",
|
||||
ChannelId: "456",
|
||||
},
|
||||
};
|
||||
const validatedResponse = getAndValidateResult(successCreateLedgerResponse, "create_ledger_channel");
|
||||
expect(validatedResponse).toEqual(successCreateLedgerResponse.result);
|
||||
});
|
||||
});
|
||||
15
dist/src/transport/http.d.ts
vendored
Normal file
15
dist/src/transport/http.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/// <reference types="node" />
|
||||
import https from "https";
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
import { NotificationMethod, NotificationParams, RequestMethod, RPCRequestAndResponses } from "../types";
|
||||
import { Transport } from ".";
|
||||
export declare class HttpTransport {
|
||||
Notifications: EventEmitter<NotificationMethod, NotificationParams>;
|
||||
static createTransport(server: string): Promise<Transport>;
|
||||
sendRequest<K extends RequestMethod>(req: RPCRequestAndResponses[K][0]): Promise<unknown>;
|
||||
Close(): Promise<void>;
|
||||
private ws;
|
||||
private server;
|
||||
private constructor();
|
||||
}
|
||||
export declare function unsecureHttpsAgent(): https.Agent;
|
||||
49
dist/src/transport/http.js
vendored
Normal file
49
dist/src/transport/http.js
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
import https from "https";
|
||||
import axios from "axios";
|
||||
import { w3cwebsocket } from "websocket";
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
import { getAndValidateNotification } from "../serde";
|
||||
export class HttpTransport {
|
||||
Notifications;
|
||||
static async createTransport(server) {
|
||||
// eslint-disable-next-line new-cap
|
||||
const ws = new w3cwebsocket(`wss://${server}/subscribe`);
|
||||
// throw any websocket errors so we don't fail silently
|
||||
ws.onerror = (e) => {
|
||||
console.error("Error with websocket connection to server: " + e);
|
||||
throw e;
|
||||
};
|
||||
// Wait for onopen to fire so we know the connection is ready
|
||||
await new Promise((resolve) => (ws.onopen = () => resolve()));
|
||||
const transport = new HttpTransport(ws, server);
|
||||
return transport;
|
||||
}
|
||||
async sendRequest(req) {
|
||||
const url = new URL(`https://${this.server}`).toString();
|
||||
const result = await axios.post(url.toString(), JSON.stringify(req));
|
||||
return result.data;
|
||||
}
|
||||
async Close() {
|
||||
this.ws.close(1000);
|
||||
}
|
||||
ws;
|
||||
server;
|
||||
constructor(ws, server) {
|
||||
this.ws = ws;
|
||||
this.server = server;
|
||||
this.Notifications = new EventEmitter();
|
||||
this.ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data.toString());
|
||||
const validatedResult = getAndValidateNotification(data.params.payload, data.method);
|
||||
this.Notifications.emit(data.method, validatedResult);
|
||||
};
|
||||
}
|
||||
}
|
||||
// For testing with self-signed certs, ignore certificate errors. DO NOT use in production.
|
||||
export function unsecureHttpsAgent() {
|
||||
// For testing with self-signed certs, ignore certificate errors. DO NOT use in production.
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
return httpsAgent;
|
||||
}
|
||||
21
dist/src/transport/index.d.ts
vendored
Normal file
21
dist/src/transport/index.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
import { NotificationMethod, NotificationParams, RequestMethod, RPCNotification, RPCRequestAndResponses } from "../types";
|
||||
export { HttpTransport } from "./http";
|
||||
export { NatsTransport } from "./nats";
|
||||
/**
|
||||
* NotificationHandler is a function that takes a notification and does something with it.
|
||||
*/
|
||||
export type NotificationHandler<T extends RPCNotification> = (notif: T) => void;
|
||||
/**
|
||||
* Transport is an interface for some kind of RPC transport.
|
||||
*/
|
||||
export type Transport = {
|
||||
Notifications: EventEmitter<NotificationMethod, NotificationParams>;
|
||||
/**
|
||||
* Send the JSON-RPC request and returns the response.
|
||||
*
|
||||
* @param req - The request to send
|
||||
*/
|
||||
sendRequest<K extends RequestMethod>(req: RPCRequestAndResponses[K][0]): Promise<unknown>;
|
||||
Close(): Promise<void>;
|
||||
};
|
||||
2
dist/src/transport/index.js
vendored
Normal file
2
dist/src/transport/index.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export { HttpTransport } from "./http";
|
||||
export { NatsTransport } from "./nats";
|
||||
14
dist/src/transport/nats.d.ts
vendored
Normal file
14
dist/src/transport/nats.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
import { NotificationMethod, NotificationParams, RequestMethod, RPCRequestAndResponses } from "../types";
|
||||
import { Transport } from ".";
|
||||
export declare class NatsTransport {
|
||||
private natsConn;
|
||||
private natsSub;
|
||||
private notifications;
|
||||
static createTransport(server: string): Promise<Transport>;
|
||||
private constructor();
|
||||
get Notifications(): EventEmitter<NotificationMethod, NotificationParams>;
|
||||
private listenForMessages;
|
||||
sendRequest<K extends RequestMethod>(req: RPCRequestAndResponses[K][0]): Promise<RPCRequestAndResponses[K][1]>;
|
||||
Close(): Promise<void>;
|
||||
}
|
||||
53
dist/src/transport/nats.js
vendored
Normal file
53
dist/src/transport/nats.js
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
import { connect, JSONCodec } from "nats";
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
const NITRO_REQUEST_TOPIC = "nitro-request";
|
||||
const NITRO_NOTIFICATION_TOPIC = "nitro-notify";
|
||||
export class NatsTransport {
|
||||
natsConn;
|
||||
natsSub;
|
||||
notifications = new EventEmitter();
|
||||
static async createTransport(server) {
|
||||
const natConn = await connect({ servers: server });
|
||||
const natsSub = natConn.subscribe(NITRO_NOTIFICATION_TOPIC);
|
||||
const transport = new NatsTransport(natConn, natsSub);
|
||||
// Start listening for messages without blocking
|
||||
transport.listenForMessages(transport.natsSub);
|
||||
return transport;
|
||||
}
|
||||
constructor(natsConn, natsSub) {
|
||||
this.natsConn = natsConn;
|
||||
this.natsSub = natsSub;
|
||||
}
|
||||
get Notifications() {
|
||||
return this.notifications;
|
||||
}
|
||||
async listenForMessages(sub) {
|
||||
for await (const msg of sub) {
|
||||
msg.data;
|
||||
const notif = JSONCodec().decode(msg.data);
|
||||
switch (notif.method) {
|
||||
case "objective_completed":
|
||||
this.notifications.emit(notif.method, notif);
|
||||
break;
|
||||
case "ledger_channel_updated":
|
||||
this.notifications.emit(notif.method, notif);
|
||||
break;
|
||||
case "payment_channel_updated":
|
||||
this.notifications.emit(notif.method, notif);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
async sendRequest(req) {
|
||||
const natsRes = await this.natsConn?.request(NITRO_REQUEST_TOPIC, JSONCodec().encode(req));
|
||||
if (!natsRes) {
|
||||
throw new Error("No response");
|
||||
}
|
||||
const decoded = JSONCodec().decode(natsRes?.data);
|
||||
return decoded;
|
||||
}
|
||||
async Close() {
|
||||
this.natsSub.unsubscribe();
|
||||
await this.natsConn.close();
|
||||
}
|
||||
}
|
||||
198
dist/src/types.d.ts
vendored
Normal file
198
dist/src/types.d.ts
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
/**
|
||||
* JSON RPC Types
|
||||
*/
|
||||
export type JsonRpcRequest<MethodName extends RequestMethod, RequestPayload> = {
|
||||
id: number;
|
||||
jsonrpc: "2.0";
|
||||
method: MethodName;
|
||||
params: {
|
||||
authtoken: string;
|
||||
payload: RequestPayload;
|
||||
};
|
||||
};
|
||||
export type JsonRpcResponse<ResultType> = {
|
||||
id: number;
|
||||
jsonrpc: "2.0";
|
||||
result: ResultType;
|
||||
};
|
||||
export type JsonRpcNotification<NotificationName, NotificationPayload> = {
|
||||
jsonrpc: "2.0";
|
||||
method: NotificationName;
|
||||
params: {
|
||||
payload: NotificationPayload;
|
||||
};
|
||||
};
|
||||
export type JsonRpcError<Code, Message, Data = undefined> = {
|
||||
id: number;
|
||||
jsonrpc: "2.0";
|
||||
error: Data extends undefined ? {
|
||||
code: Code;
|
||||
message: Message;
|
||||
} : {
|
||||
code: Code;
|
||||
message: Message;
|
||||
data: Data;
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Objective payloads and responses
|
||||
*/
|
||||
export type DirectFundPayload = {
|
||||
CounterParty: string;
|
||||
ChallengeDuration: number;
|
||||
Outcome: Outcome;
|
||||
Nonce: number;
|
||||
AppDefinition: string;
|
||||
AppData: string;
|
||||
};
|
||||
export type VirtualFundPayload = {
|
||||
Intermediaries: string[];
|
||||
CounterParty: string;
|
||||
ChallengeDuration: number;
|
||||
Outcome: Outcome;
|
||||
Nonce: number;
|
||||
AppDefinition: string;
|
||||
};
|
||||
export type PaymentPayload = {
|
||||
Amount: number;
|
||||
Channel: string;
|
||||
};
|
||||
export type Voucher = {
|
||||
ChannelId: string;
|
||||
Amount: number;
|
||||
Signature: string;
|
||||
};
|
||||
type GetChannelRequest = {
|
||||
Id: string;
|
||||
};
|
||||
type GetByLedgerRequest = {
|
||||
LedgerId: string;
|
||||
};
|
||||
export type DefundObjectiveRequest = {
|
||||
ChannelId: string;
|
||||
};
|
||||
export type ObjectiveResponse = {
|
||||
Id: string;
|
||||
ChannelId: string;
|
||||
};
|
||||
export type ReceiveVoucherResult = {
|
||||
Total: bigint;
|
||||
Delta: bigint;
|
||||
};
|
||||
/**
|
||||
* RPC Requests
|
||||
*/
|
||||
export type GetAuthTokenRequest = JsonRpcRequest<"get_auth_token", Record<string, never>>;
|
||||
export type GetAddressRequest = JsonRpcRequest<"get_address", Record<string, never>>;
|
||||
export type DirectFundRequest = JsonRpcRequest<"create_ledger_channel", DirectFundPayload>;
|
||||
export type PaymentRequest = JsonRpcRequest<"pay", PaymentPayload>;
|
||||
export type VirtualFundRequest = JsonRpcRequest<"create_payment_channel", VirtualFundPayload>;
|
||||
export type GetLedgerChannelRequest = JsonRpcRequest<"get_ledger_channel", GetChannelRequest>;
|
||||
export type GetAllLedgerChannelsRequest = JsonRpcRequest<"get_all_ledger_channels", Record<string, never>>;
|
||||
export type GetPaymentChannelRequest = JsonRpcRequest<"get_payment_channel", GetChannelRequest>;
|
||||
export type GetPaymentChannelsByLedgerRequest = JsonRpcRequest<"get_payment_channels_by_ledger", GetByLedgerRequest>;
|
||||
export type VersionRequest = JsonRpcRequest<"version", Record<string, never>>;
|
||||
export type DirectDefundRequest = JsonRpcRequest<"close_ledger_channel", DefundObjectiveRequest>;
|
||||
export type VirtualDefundRequest = JsonRpcRequest<"close_payment_channel", DefundObjectiveRequest>;
|
||||
export type CreateVoucherRequest = JsonRpcRequest<"create_voucher", PaymentPayload>;
|
||||
export type ReceiveVoucherRequest = JsonRpcRequest<"receive_voucher", Voucher>;
|
||||
/**
|
||||
* RPC Responses
|
||||
*/
|
||||
export type GetAuthTokenResponse = JsonRpcResponse<string>;
|
||||
export type GetPaymentChannelResponse = JsonRpcResponse<PaymentChannelInfo>;
|
||||
export type PaymentResponse = JsonRpcResponse<PaymentPayload>;
|
||||
export type GetLedgerChannelResponse = JsonRpcResponse<LedgerChannelInfo>;
|
||||
export type VirtualFundResponse = JsonRpcResponse<ObjectiveResponse>;
|
||||
export type VersionResponse = JsonRpcResponse<string>;
|
||||
export type GetAddressResponse = JsonRpcResponse<string>;
|
||||
export type DirectFundResponse = JsonRpcResponse<ObjectiveResponse>;
|
||||
export type DirectDefundResponse = JsonRpcResponse<string>;
|
||||
export type VirtualDefundResponse = JsonRpcResponse<string>;
|
||||
export type GetAllLedgerChannelsResponse = JsonRpcResponse<LedgerChannelInfo[]>;
|
||||
export type GetPaymentChannelsByLedgerResponse = JsonRpcResponse<PaymentChannelInfo[]>;
|
||||
export type CreateVoucherResponse = JsonRpcResponse<Voucher>;
|
||||
export type ReceiveVoucherResponse = JsonRpcResponse<ReceiveVoucherResult>;
|
||||
/**
|
||||
* RPC Request/Response map
|
||||
* This is a map of all the RPC methods to their request and response types
|
||||
*/
|
||||
export type RPCRequestAndResponses = {
|
||||
get_auth_token: [GetAuthTokenRequest, GetAuthTokenResponse];
|
||||
create_ledger_channel: [DirectFundRequest, DirectFundResponse];
|
||||
close_ledger_channel: [DirectDefundRequest, DirectDefundResponse];
|
||||
version: [VersionRequest, VersionResponse];
|
||||
create_payment_channel: [VirtualFundRequest, VirtualFundResponse];
|
||||
get_address: [GetAddressRequest, GetAddressResponse];
|
||||
get_ledger_channel: [GetLedgerChannelRequest, GetLedgerChannelResponse];
|
||||
get_payment_channel: [GetPaymentChannelRequest, GetPaymentChannelResponse];
|
||||
pay: [PaymentRequest, PaymentResponse];
|
||||
close_payment_channel: [VirtualDefundRequest, VirtualDefundResponse];
|
||||
get_all_ledger_channels: [
|
||||
GetAllLedgerChannelsRequest,
|
||||
GetAllLedgerChannelsResponse
|
||||
];
|
||||
get_payment_channels_by_ledger: [
|
||||
GetPaymentChannelsByLedgerRequest,
|
||||
GetPaymentChannelsByLedgerResponse
|
||||
];
|
||||
create_voucher: [CreateVoucherRequest, CreateVoucherResponse];
|
||||
receive_voucher: [ReceiveVoucherRequest, ReceiveVoucherResponse];
|
||||
};
|
||||
export type RequestMethod = keyof RPCRequestAndResponses;
|
||||
export type RPCRequest = RPCRequestAndResponses[keyof RPCRequestAndResponses][0];
|
||||
export type RPCResponse = RPCRequestAndResponses[keyof RPCRequestAndResponses][1];
|
||||
/**
|
||||
* RPC Notifications
|
||||
*/
|
||||
export type RPCNotification = ObjectiveCompleteNotification | PaymentChannelUpdatedNotification | LedgerChannelUpdatedNotification;
|
||||
export type NotificationMethod = RPCNotification["method"];
|
||||
export type NotificationParams = RPCNotification["params"];
|
||||
export type PaymentChannelUpdatedNotification = JsonRpcNotification<"payment_channel_updated", PaymentChannelInfo>;
|
||||
export type LedgerChannelUpdatedNotification = JsonRpcNotification<"ledger_channel_updated", LedgerChannelInfo>;
|
||||
export type ObjectiveCompleteNotification = JsonRpcNotification<"objective_completed", string>;
|
||||
/**
|
||||
* Outcome related types
|
||||
*/
|
||||
export type LedgerChannelInfo = {
|
||||
ID: string;
|
||||
Status: ChannelStatus;
|
||||
Balance: LedgerChannelBalance;
|
||||
};
|
||||
export type LedgerChannelBalance = {
|
||||
AssetAddress: string;
|
||||
Them: string;
|
||||
Me: string;
|
||||
TheirBalance: bigint;
|
||||
MyBalance: bigint;
|
||||
};
|
||||
export type PaymentChannelBalance = {
|
||||
AssetAddress: string;
|
||||
Payee: string;
|
||||
Payer: string;
|
||||
PaidSoFar: bigint;
|
||||
RemainingFunds: bigint;
|
||||
};
|
||||
export type PaymentChannelInfo = {
|
||||
ID: string;
|
||||
Status: ChannelStatus;
|
||||
Balance: PaymentChannelBalance;
|
||||
};
|
||||
export type Outcome = SingleAssetOutcome[];
|
||||
export type SingleAssetOutcome = {
|
||||
Asset: string;
|
||||
AssetMetadata: AssetMetadata;
|
||||
Allocations: Allocation[];
|
||||
};
|
||||
export type Allocation = {
|
||||
Destination: string;
|
||||
Amount: number;
|
||||
AllocationType: number;
|
||||
Metadata: null;
|
||||
};
|
||||
export type AssetMetadata = {
|
||||
AssetType: number;
|
||||
Metadata: null;
|
||||
};
|
||||
export type ChannelStatus = "Proposed" | "Open" | "Closing" | "Complete";
|
||||
export {};
|
||||
1
dist/src/types.js
vendored
Normal file
1
dist/src/types.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
34
dist/src/utils.d.ts
vendored
Normal file
34
dist/src/utils.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
import { NitroRpcClient } from "./rpc-client";
|
||||
import { Outcome, RequestMethod, RPCRequestAndResponses } from "./types";
|
||||
export declare const RPC_PATH = "api/v1";
|
||||
/**
|
||||
* createOutcome creates a basic outcome for a channel
|
||||
*
|
||||
* @param asset - The asset to fund the channel with
|
||||
* @param alpha - The address of the first participant
|
||||
* @param beta - The address of the second participant
|
||||
* @param amount - The amount to allocate to each participant
|
||||
* @returns An outcome for a directly funded channel with 100 wei allocated to each participant
|
||||
*/
|
||||
export declare function createOutcome(asset: string, alpha: string, beta: string, amount: number): Outcome;
|
||||
/**
|
||||
* Left pads a 20 byte address hex string with zeros until it is a 32 byte hex string
|
||||
* e.g.,
|
||||
* 0x9546E319878D2ca7a21b481F873681DF344E0Df8 becomes
|
||||
* 0x0000000000000000000000009546E319878D2ca7a21b481F873681DF344E0Df8
|
||||
*
|
||||
* @param address - 20 byte hex string
|
||||
* @returns 32 byte padded hex string
|
||||
*/
|
||||
export declare function convertAddressToBytes32(address: string): string;
|
||||
/**
|
||||
* generateRequest is a helper function that generates a request object for the given method and payloads
|
||||
*
|
||||
* @param method - The RPC method to generate a request for
|
||||
* @param payload - The payloads to include in the request
|
||||
* @returns A request object of the correct type
|
||||
*/
|
||||
export declare function generateRequest<K extends RequestMethod, T extends RPCRequestAndResponses[K][0]>(method: K, payload: T["params"]["payload"], authToken: string): T;
|
||||
export declare function getLocalRPCUrl(port: number): string;
|
||||
export declare function logOutChannelUpdates(rpcClient: NitroRpcClient): Promise<void>;
|
||||
export declare function compactJson(obj: unknown): string;
|
||||
82
dist/src/utils.js
vendored
Normal file
82
dist/src/utils.js
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
export const RPC_PATH = "api/v1";
|
||||
/**
|
||||
* createOutcome creates a basic outcome for a channel
|
||||
*
|
||||
* @param asset - The asset to fund the channel with
|
||||
* @param alpha - The address of the first participant
|
||||
* @param beta - The address of the second participant
|
||||
* @param amount - The amount to allocate to each participant
|
||||
* @returns An outcome for a directly funded channel with 100 wei allocated to each participant
|
||||
*/
|
||||
export function createOutcome(asset, alpha, beta, amount) {
|
||||
return [
|
||||
{
|
||||
Asset: asset,
|
||||
AssetMetadata: {
|
||||
AssetType: 0,
|
||||
Metadata: null,
|
||||
},
|
||||
Allocations: [
|
||||
{
|
||||
Destination: convertAddressToBytes32(alpha),
|
||||
Amount: amount,
|
||||
AllocationType: 0,
|
||||
Metadata: null,
|
||||
},
|
||||
{
|
||||
Destination: convertAddressToBytes32(beta),
|
||||
Amount: amount,
|
||||
AllocationType: 0,
|
||||
Metadata: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Left pads a 20 byte address hex string with zeros until it is a 32 byte hex string
|
||||
* e.g.,
|
||||
* 0x9546E319878D2ca7a21b481F873681DF344E0Df8 becomes
|
||||
* 0x0000000000000000000000009546E319878D2ca7a21b481F873681DF344E0Df8
|
||||
*
|
||||
* @param address - 20 byte hex string
|
||||
* @returns 32 byte padded hex string
|
||||
*/
|
||||
export function convertAddressToBytes32(address) {
|
||||
const digits = address.startsWith("0x") ? address.substring(2) : address;
|
||||
return `0x${digits.padStart(24, "0")}`;
|
||||
}
|
||||
/**
|
||||
* generateRequest is a helper function that generates a request object for the given method and payloads
|
||||
*
|
||||
* @param method - The RPC method to generate a request for
|
||||
* @param payload - The payloads to include in the request
|
||||
* @returns A request object of the correct type
|
||||
*/
|
||||
export function generateRequest(method, payload, authToken) {
|
||||
return {
|
||||
jsonrpc: "2.0",
|
||||
method,
|
||||
params: { authtoken: authToken, payload: payload },
|
||||
// Our schema defines id as a uint32. We mod the current time to ensure that we don't overflow
|
||||
id: Date.now() % 1_000_000_000,
|
||||
}; // TODO: We shouldn't have to cast here
|
||||
}
|
||||
export function getLocalRPCUrl(port) {
|
||||
return `127.0.0.1:${port}/${RPC_PATH}`;
|
||||
}
|
||||
export async function logOutChannelUpdates(rpcClient) {
|
||||
const shortAddress = (await rpcClient.GetAddress()).slice(0, 8);
|
||||
rpcClient.Notifications.on("ledger_channel_updated", (info) => {
|
||||
console.log(`${shortAddress}: Ledger channel update\n${prettyJson(info)}`);
|
||||
});
|
||||
rpcClient.Notifications.on("payment_channel_updated", (info) => {
|
||||
console.log(`${shortAddress}: Payment channel update\n${prettyJson(info)}`);
|
||||
});
|
||||
}
|
||||
function prettyJson(obj) {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
}
|
||||
export function compactJson(obj) {
|
||||
return JSON.stringify(obj, null, 0);
|
||||
}
|
||||
1
dist/tsconfig.tsbuildinfo
vendored
Normal file
1
dist/tsconfig.tsbuildinfo
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user