A complete, fast and slim SDK to interact with the Pocket Network.
You can install the packages using your favorite package manager like yarn, npm, or pnpm. See the usages below to know which package needs to be installed in your project.
yarn add @pokt-foundation/pocketjs-provider
yarn add @pokt-foundation/pocketjs-signer
yarn add @pokt-foundation/pocketjs-transaction-builder
yarn add @pokt-foundation/pocketjs-relayer
yarn add @pokt-foundation/pocketjs-utils
This example queries the latest height, a wallet's balance, and the list of
jailed nodes in the network specified by PoktEndpoint
.
PoktEndpoint
is a string representing an endpoint URL to any Pocket-based
network, POKT Mainnet, Testnet or your own devnet. It may or may not contain
basic authentication credentials like "https://scott:tiger@example.pokt.network".
You can get an endpoint to POKT Mainnet from one of Pocket's Gateways. See
https://docs.pokt.network/developers/use-a-gateway for details. For example,
Grove's endpoint is like https://mainnet.rpc.grove.city/v1/<AccessKey>
.
import "dotenv/config";
import { JsonRpcProvider } from "@pokt-foundation/pocketjs-provider";
import { JailedStatus } from "@pokt-foundation/pocketjs-types";
const PoktEndpoint = process.env.POKT_ENDPOINT;
async function main() {
const provider = new JsonRpcProvider({
rpcUrl: PoktEndpoint,
});
// Get the latest height
const height = await provider.getBlockNumber();
console.log(height);
// Get the balance of a wallet in μPOKT
const balance = await provider.getBalance(
"85efd04b9bad9da612ee2f80db9b62bb413e32fb",
);
console.log(balance);
const nodes = await provider.getNodes({
blockHeight: 0,
page: 1,
perPage: 100,
jailedStatus: JailedStatus.Jailed,
})
console.log(nodes.data.map(n => n.address));
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Output:
127561
24301704832n
[
'b30d23caf048ac466e46fcd54c397631a377b522',
'b47e9f313fa12d91975b62aedf648dd21410efe9'
]
This example sends 1 POKT from a wallet associated with SIGNER_PRIVATE_KEY
to the address fc1c79e7efecf89f071ebb2ba7c6f5a98dcdfc3c
in POKT Testnet and
prints the transaction hash.
To send tokens, a send message must be signed by a sender account's private key.
There are two ways to import a private key into TransactionBuilder
.
-
KeyManager.fromPrivateKey
takes a private key in a raw format. You can get it from "pocket accounts export-raw" command. This example demonstrates this usecase. -
KeyManager.fromPPK
takes the content of an armored keypair file and its passphrade. A keypair file can be exported by "pocket accounts export" command. You can see this usecase in the next example.
import "dotenv/config";
import { JsonRpcProvider } from "@pokt-foundation/pocketjs-provider";
import { KeyManager } from "@pokt-foundation/pocketjs-signer";
import { TransactionBuilder } from "@pokt-foundation/pocketjs-transaction-builder";
async function main() {
const provider = new JsonRpcProvider({rpcUrl: process.env.POKT_ENDPOINT});
const txSignerKey = process.env.SIGNER_PRIVATE_KEY;
const txSigner = await KeyManager.fromPrivateKey(txSignerKey);
const transactionBuilder = new TransactionBuilder({
provider,
signer: txSigner,
// To submit a transaction to a non-Mainnet network, you need to specify
// the network ID in `chainID`.
chainID: "testnet",
});
// Create a message to send POKT
const txMsg = transactionBuilder.send({
fromAddress: txSigner.getAddress(),
toAddress: "fc1c79e7efecf89f071ebb2ba7c6f5a98dcdfc3c",
// Amount in μPOKT ("1000000" means 1 POKT)
amount: "1000000",
});
// Submit the message as a transaction
const txResponse = await transactionBuilder.submit({
txMsg,
memo: "Send 1 POKT via PocketJS",
});
console.log(txResponse.txHash);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Output:
2958AC49C3C00DD9F3E6FF2C7983D56E93B4164FF197F6D9DD6954A0BF4FD066
transactionBuilder.nodeStake
creates a message to stake a new node or
edit a staked node.
This examples stakes a node with the output address and delegators in POKT Testnet.
A signer's private is imported from an armored keypair file "pocket-account-e95c3df1649d9525ae65949eb4c8466ee7ee8bef.json".
import "dotenv/config";
import { JsonRpcProvider } from "@pokt-foundation/pocketjs-provider";
import { KeyManager } from "@pokt-foundation/pocketjs-signer";
import { TransactionBuilder } from "@pokt-foundation/pocketjs-transaction-builder";
import ArmoredJson from './pocket-account-e95c3df1649d9525ae65949eb4c8466ee7ee8bef.json' assert { type: 'json' };
async function main() {
const provider = new JsonRpcProvider({rpcUrl: process.env.POKT_ENDPOINT});
const txSigner = await KeyManager.fromPPK({
ppk: JSON.stringify(ArmoredJson),
password: "password",
});
const transactionBuilder = new TransactionBuilder({
provider,
signer: txSigner,
chainID: "testnet",
});
// Create a message to stake a node with delegators
const txMsg = transactionBuilder.nodeStake({
amount: "16000000000",
chains: ["0001", "0002"],
serviceURL: new URL("https://node1.testnet.pokt.network:443"),
outputAddress: "fc1c79e7efecf89f071ebb2ba7c6f5a98dcdfc3c",
rewardDelegators: {
"8147ed5182da6e7dea33f36d78db6327f9df6ba0": 10,
"54751ae3431c015a6e24d711c9d1ed4e5a276479": 50,
}
});
const txResponse = await transactionBuilder.submit({
txMsg,
memo: "NodeStake via PocketJS",
});
console.log(txResponse.txHash);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Output:
E85F8D495722B9832B9FA36E2F41012737A8FFF2DD2428A8F9C22847907032C7
There are some variations of a node-stake message.
-
Stake from the output address
Instantiate
TransactionBuilder
with the private key of the output address and specify the node's public key innodePubKey
.const transactionBuilder = new TransactionBuilder({ provider, signer: txOutputSigner, // this must be the key of the output address chainID: "testnet", }); const txMsg = transactionBuilder.nodeStake({ nodePubKey: "f53b9120f4f18c09f883e82b5c1554eddb78cd56eb753eb2ae0dfdbc492cfaaf", amount: "16000000000", chains: ["0001", "0002"], serviceURL: new URL("https://node1.testnet.pokt.network:443"), outputAddress: "fc1c79e7efecf89f071ebb2ba7c6f5a98dcdfc3c", rewardDelegators: { "8147ed5182da6e7dea33f36d78db6327f9df6ba0": 10, "54751ae3431c015a6e24d711c9d1ed4e5a276479": 50, } });
-
Stake without delegators or remove delegators from a staked node
Simply skip
rewardDelegators
to create a message. If you submit this transaction to an existing node with delegators, this removes the existing delegators from the node.const txMsg = transactionBuilder.nodeStake({ amount: "16000000000", chains: ["0001", "0002"], serviceURL: new URL("https://node1.testnet.pokt.network:443"), outputAddress: "fc1c79e7efecf89f071ebb2ba7c6f5a98dcdfc3c", });
This examples stakes an app with 1000 POKT in POKT Testnet.
import "dotenv/config";
import { JsonRpcProvider } from "@pokt-foundation/pocketjs-provider";
import { KeyManager } from "@pokt-foundation/pocketjs-signer";
import { TransactionBuilder } from "@pokt-foundation/pocketjs-transaction-builder";
async function main() {
const provider = new JsonRpcProvider({rpcUrl: process.env.POKT_ENDPOINT});
const txSignerKey = process.env.SIGNER_PRIVATE_KEY;
const txSigner = await KeyManager.fromPrivateKey(txSignerKey);
const transactionBuilder = new TransactionBuilder({
provider,
signer: txSigner,
// To submit a transaction to a non-Mainnet network, you need to specify
// the network ID in `chainID`.
chainID: "testnet",
});
// Create a message to stake an app
const txMsg = transactionBuilder.appStake({
appPubKey: "f53b9120f4f18c09f883e82b5c1554eddb78cd56eb753eb2ae0dfdbc492cfaaf",
chains: ["0001", "0002"],
// Amount in μPOKT ("1000000000" means 1000 POKT)
amount: "1000000000",
});
const txResponse = await transactionBuilder.submit({
txMsg,
memo: "AppStake via PocketJS",
});
console.log(txResponse.txHash);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Output:
137BF72DAB2EA4DE9D27B25FFBCDC13C072687F6938F9530116FE96C91DC8F51
You can directly transfer the slot of a staked app to a new empty address without unstaking the app.
This examples transfers the slot of a staked app from the address associated
with process.env.SIGNER_PRIVATE_KEY
to fc1c79e7efecf89f071ebb2ba7c6f5a98dcdfc3c
that is the address of the public key
0c872497365fad64c3909c934983853865b79e50fe7b8b8003a47baf99d5a64d
.
import "dotenv/config";
import { JsonRpcProvider } from "@pokt-foundation/pocketjs-provider";
import { KeyManager } from "@pokt-foundation/pocketjs-signer";
import { TransactionBuilder } from "@pokt-foundation/pocketjs-transaction-builder";
async function main() {
const provider = new JsonRpcProvider({rpcUrl: process.env.POKT_ENDPOINT});
const txSignerKey = process.env.SIGNER_PRIVATE_KEY;
const txSigner = await KeyManager.fromPrivateKey(txSignerKey);
const transactionBuilder = new TransactionBuilder({
provider,
signer: txSigner,
// To submit a transaction to a non-Mainnet network, you need to specify
// the network ID in `chainID`.
chainID: "testnet",
});
// Create a message to transfer the app slot
const txMsg = transactionBuilder.appTransfer({
appPubKey: "0c872497365fad64c3909c934983853865b79e50fe7b8b8003a47baf99d5a64d",
});
const txResponse = await transactionBuilder.submit({
txMsg,
memo: "AppTransfer via PocketJS",
});
console.log(txResponse.txHash);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Output:
AAF82443ADF0D5B0F43DE8E3537BF140B8F4122949F342D4831A57790AE0215D
This example submits the call of eth_chainId
to the chain 005A
in the
network specified by process.env.POKT_ENDPOINT
.
import "dotenv/config";
import { JsonRpcProvider } from "@pokt-foundation/pocketjs-provider";
import { KeyManager } from "@pokt-foundation/pocketjs-signer";
import { Relayer } from "@pokt-foundation/pocketjs-relayer";
const AppPrivateKey = process.env.APP_PRIVATE_KEY;
async function main() {
// Client signer adds a signature to every relay request. This example
// generates a new key every time for demonstration purpose.
const clientSigner = await KeyManager.createRandom();
// AAT (= Application Authentication Token) must be attached to every relay
// request. Otherwise it's rejected by the network. To generate an AAT,
// you need to have the private key of a staked application.
// This example generates an AAT every request for demonstration purpose.
// Usually a relayer pre-creates an AAT with Client public key, which is
// also pre-created, and uses the AAT and the Client private key in the
// relayer.
const appSigner = await KeyManager.fromPrivateKey(AppPrivateKey);
const aat = await Relayer.GenerateAAT(
appSigner,
clientSigner.publicKey,
);
// To use `Relayer`, you need to specify `dispatchers`.
const provider = new JsonRpcProvider({
dispatchers: [process.env.POKT_ENDPOINT],
});
const relayer = new Relayer({
keyManager: clientSigner,
provider,
});
const chain = '005A';
const payload = {"id":1,"jsonrpc":"2.0","method":"eth_chainId"};
const sessionResp = await provider.dispatch({
sessionHeader: {
sessionBlockHeight: 0,
chain,
applicationPubKey: appSigner.publicKey,
},
});
const relayResp = await relayer.relay({
blockchain: chain,
data: JSON.stringify(payload),
pocketAAT: aat,
session: sessionResp.session,
options: {
retryAttempts: 5,
rejectSelfSignedCertificates: false,
timeout: 8000,
},
});
console.log(relayResp.response);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Output:
{"jsonrpc":"2.0","id":1,"result":"0xc0d31"}
Clone the repo from Github, and just run pnpm i
at the root folder. This will install all of the individual packages in the necessary order. If you'd like to build all packages manually, you'll need follow the order in which they're referenced:
- Install
packages/utils
- Install
packages/types
- Install
packages/abstract-provider
- Install
packages/provider
- Install
packages/signer
- Install
packages/relayer
- Install
packages/transaction-builder
The best way to develop locally is to link the local packages from your clone to the project you're working on; this will let you either run the dev server or build the packages to see the changes in real time.
We use Turborepo to manage build caches and our general build/test pipeline. This means that only the packages that have changed will get built again, saving you time. At the root package.json
we've also defined a collection of scripts you can use to run individual packages on their dev server.
For running tests, either run pnpm jest
on the corresponding repo or run pnpm turbo run test
on the root folder to run all tests.
👋 Get started contributing with a good first issue.
Don't be shy to contribute even the smallest tweak. 🐲 There are still some dragons to be aware of, but we'll be here to help you get started!
If you ever come across an issue with PocketJS, do a search in the issues tab of this repo and make sure it hasn't been reported before. Follow these steps to help us prevent unnecessary notifications to the many people following this repo.
- If the issue you found has been reported and is still open, and the details match your issue, give a "thumbs up" to the relevant posts in the issue thread to signal that you have the same issue. No further action is required on your part.
- If the issue you found has been reported and is still open, but the issue is missing some details, you can add a comment to the issue thread describing the additional details.
- If the issue you found has been reported but has been closed, you can comment on the closed issue thread and ask to have the issue reopened because you are still experiencing the issue. Alternatively, you can open a new issue, reference the closed issue by number or link, and state that you are still experiencing the issue. Provide any additional details in your post so we can better understand the issue and how to fix it.