The Sentinel JS SDK relays on CosmJS and it was developed following the official tutorial, it take also inspiration from other open source Cosmos SDK.
The SDK exstends the followings:
SentinelClientSigningSentinelClientclient.sentinelQueryFull documentation: https://sentinel-official.github.io/sentinel-js-sdk/
The SDK supports every service type advertised by sentinel-dvpnx v9:
| Protocol | SDK class | Client mode | Runtime executable |
|---|---|---|---|
| WireGuard | Wireguard |
Full tunnel | wg-quick |
| V2Ray | V2Ray |
Local SOCKS5 proxy | v2ray |
| OpenVPN | OpenVPN |
Full tunnel | openvpn |
| Xray | Xray |
Local SOCKS5 proxy | xray |
| AmneziaWG | AmneziaWG |
Full tunnel | awg-quick |
| Hysteria2 | Hysteria2 |
Full tunnel | hysteria2 |
The SDK creates protocol credentials, parses the authenticated node handshake,
writes private configuration files (0600 on Unix), and starts/stops the
corresponding client. Native executables are not bundled in the npm package.
Full-tunnel clients require root/administrator network privileges.
The connection flow is the same for all protocols:
nodeInfo(remoteAddr).service_type.NodeEventCreateSession.handshake.result.data, then call parseConfig.connect; later call disconnect and cleanup.import {
AmneziaWG,
Hysteria2,
NodeVPNType,
OpenVPN,
V2Ray,
Wireguard,
Xray,
handshake,
} from "@sentinel-official/sentinel-js-sdk";
const clients = {
[NodeVPNType.WIREGUARD]: () => new Wireguard(),
[NodeVPNType.V2RAY]: () => new V2Ray(),
[NodeVPNType.OPENVPN]: () => new OpenVPN(),
[NodeVPNType.XRAY]: () => new Xray(),
[NodeVPNType.AMNEZIAWG]: () => new AmneziaWG(),
[NodeVPNType.HYSTERIA2]: () => new Hysteria2(),
};
const serviceType = nodeStatus.service_type;
const vpn = clients[serviceType]();
const result = await handshake(
sessionId,
vpn.getPeerRequest(),
cosmosPrivateKey,
node.remoteAddrs[0],
);
const data = JSON.parse(Buffer.from(result.data, "base64").toString("utf8"));
await vpn.parseConfig(data, result.addrs);
await vpn.connect();
V2Ray and Xray expose the selected local proxy port as vpn.socksPort.
Their traffic must be routed through socks5://127.0.0.1:<socksPort>; they do
not alter the system default route.
import { SentinelClient } from "@sentinel-official/sentinel-js-sdk";
const rpc = "https://rpc.sentinel.co:443"
const client = await SentinelClient.connect(rpc)
If you need to sign and broadcast a tx you need to instantiate a SigningClient.
import { SigningSentinelClient } from "@sentinel-official/sentinel-js-sdk";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing"
import { GasPrice } from "@cosmjs/stargate"
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "sent" });
const [account] = await wallet.getAccounts();
const rpc = "https://rpc.sentinel.co:443"
// With a default GasPrice:
const customDenom = "udvpn"
const gasPrice = GasPrice.fromString(`0.2${customDenom}`);
const client = await SigningSentinelClient.connectWithSigner(rpc, wallet, {
gasPrice: gasPrice
})
// Without a default GasPrice:
const client = await SigningSentinelClient.connectWithSigner(rpc, wallet)
You can also set other default parameters: https://cosmos.github.io/cosmjs/latest/stargate/interfaces/SigningStargateClientOptions.html.
In order to subscribe to chain events you need a websocket client
import { SentinelWsClient } from "@sentinel-official/sentinel-js-sdk";
const client = new SentinelWsClient("wss://rpc.sentinel.quokkastake.io");
const stream = client.subscribe("tm.event='NewBlock'")
stream.addListener({
next: data => console.log(data),
error: err => console.error(err),
complete: () => console.log('completed'),
});
import { Status, PageRequest } from "@sentinel-official/sentinel-js-sdk";
const nodes = await client.sentinelQuery?.node.nodes(
Status.STATUS_ACTIVE,
PageRequest.fromPartial({
limit: 5,
countTotal: true
})
)
For pagination please follow:
After you have initialize a SigningClient you can prepare and broadcast a tx in multiple ways.
signAndBroadcast from your client.import { TxNodeStartSession, nodeStartSession } from "@sentinel-official/sentinel-js-sdk";
import Long from "long";
const udvpn = p2pNode.node.gigabytePrices.filter(x => x.denom === "udvpn")[0] as Price
const args: TxNodeStartSession = {
from: account.address,
nodeAddress: p2pNode.node.address,
gigabytes: Long.fromString(gygabytes, true),
maxPrice: udvpn
}
const msg = nodeStartSession(args)
const tx = client.signAndBroadcast(account.address, [msg], "auto", "memo")
nodeStartSession from your client or from submodule (this will automatically signAndBroadcast the tx)import { TxNodeStartSession } from "@sentinel-official/sentinel-js-sdk";
import Long from "long";
const args: TxNodeStartSession = {
from: account.address,
nodeAddress: sentnode,
gigabytes: Long.fromNumber(gygabyte, true),
fee: "auto",
memo: "hello from js-sdk"
maxPrice: ....
}
// call directly
const tx1 = client.nodeStartSession(args)
// use submodule
const tx2 = client.node.startSession(args)
It is up to you if you want to await the tx or use a callback
const tx1 = await client.nodeStartSession(args)
client.nodeStartSession(args).then(tx => { do stuff... })
client.signAndBroadcast or implicit call trought, for example, client.nodeStartSession or client.node.subscribe return a DeliverTxResponse. You can search for a determinate event using the method searchEvent. For example if you are looking for sentinel.node.v3.EventCreateSession, you can do the following:
import { searchEvent } from "@sentinel-official/sentinel-js-sdk";
const eventCreateSubscription = searchEvent(sentinel.node.v3.EventCreateSession, tx.events);
or better
import { searchEvent } from "@sentinel-official/sentinel-js-sdk";
import { NodeEventCreateSession } from "@sentinel-official/sentinel-js-sdk";
const eventCreateSubscription = searchEvent(NodeEventCreateSession.type, tx.events);
The single event can also be parsed via the appropriate interface, following the example:
import { searchEvent } from "@sentinel-official/sentinel-js-sdk";
import { NodeEventCreateSession, isNodeEventCreateSession } from "@sentinel-official/sentinel-js-sdk";
const eventCreateSubscription = searchEvent(NodeEventCreateSession.type, tx.events);
if(eventCreateSubscription && isNodeEventCreateSession(eventCreateSubscription)) {
const eventParsed = NodeEventCreateSession.parse(eventCreateSubscription)
console.log(`Your subscription id is: ${eventParsed.value.sessionId}`)
} else console.log("eventCreateSubscription, not found")
All the .proto files are compiled using protoc and ts-proto as plugin. The compiled .ts proto files are under src/protobuf. If you want to compile again you can use scripts/generate-proto.sh. The script requires git and protoc, it will automatically download all the .proto definitions from sentinel-hub and relative third parties.
The official cosmjs examples can be used as well.
Just remember to replace StargateClient with SentinelClient and SigningStargateClient with SigningSentinelClient.
The repository provide currently a nodejs script example and a keplr one (based on: https://tutorials.cosmos.network/tutorials/7-cosmjs/4-with-keplr.html).
The opt-in mainnet VPN smoke test discovers nodes dynamically and verifies session creation, handshake, configuration, connectivity, cleanup, and cancellation. It is not run in CI: it spends wallet funds and some protocols require root network privileges.
If you want to use a local version of sdk (for testing purpose) you have to compile the src folder into dist and then link the package with npm.
npm run build
npm link
cd examples/<choose-an-example>
npm link @sentinel-official/sentinel-js-sdk
cd examples/keplr
npm i
npm run dev
Navigate to: http://127.0.0.1:3000/.
cd examples/node
npm i
ts-node main.ts
Generated using TypeDoc