concordium-node-sdk-js

Wrappers for interacting with the Concordium node.
ConcordiumNodeClient
The ConcordiumNodeClient defines the interface to be used to send and receive data from
a concordium-node.
Creating a client
The current node setup only allows for insecure connections, which can be set up in the following way.
The access is controlled by the credentials and the metadata.
import { credentials, Metadata } from "@grpc/grpc-js";
import { ConcordiumNodeClient } from "@concordium/node-sdk";
const metadata = new Metadata();
metadata.add("authentication", "rpcadmin");
const insecureCredentials = credentials.createInsecure();
const client = new ConcordiumNodeClient(
"127.0.0.1",
10000,
insecureCredentials,
metadata,
15000
);
Create a simple transfer
The following example demonstrates how a simple transfer can be created.
const header: AccountTransactionHeader = {
expiry: new TransactionExpiry(new Date(Date.now() + 3600000)),
nonce: 1n,
sender: new AccountAddress("4ZJBYQbVp3zVZyjCXfZAAYBVkJMyVj8UKUNj9ox5YqTCBdBq2M"),
};
const simpleTransfer: SimpleTransferPayload = {
amount: new GtuAmount(100n),
toAddress: new AccountAddress("4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf"),
};
const simpleTransferAccountTransaction: AccountTransaction = {
header: header,
payload: simpleTransfer,
type: AccountTransactionType.SimpleTransfer,
};
Create a simple transfer with a memo
The following example demonstrates how a simple transfer with a memo can be created.
const header: AccountTransactionHeader = {
expiry: new TransactionExpiry(new Date(Date.now() + 3600000)),
nonce: 1n,
sender: new AccountAddress("4ZJBYQbVp3zVZyjCXfZAAYBVkJMyVj8UKUNj9ox5YqTCBdBq2M"),
};
const simpleTransferWithMemo: SimpleTransferWithMemoPayload = {
amount: new GtuAmount(100n),
toAddress: new AccountAddress("4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf"),
memo: new Memo(Buffer.from('6B68656C6C6F20776F726C64', 'hex')),
};
const simpleTransferWithMemoAccountTransaction: AccountTransaction = {
header: header,
payload: simpleTransferWithMemo,
type: AccountTransactionType.SimpleTransferWithMemo,
};
Send Account Transaction
The following example demonstrates how to send any account transaction.
See the previous sections for how to create an account transaction.
import * as ed from "noble-ed25519";
let accountTransaction: AccountTransaction;
const signingKey = "ce432f6bba0d47caec1f45739331dc354b6d749fdb8ab7c2b7f6cb24db39ca0c";
const hashToSign = getAccountTransactionSignDigest(accountTransaction);
const signature = Buffer.from(await ed.sign(hashToSign, signingKey)).toString("hex");
const signatures: AccountTransactionSignature = {
0: {
0: signature
}
};
const success = await client.sendAccountTransaction(accountTransaction, signatures);
if (success) {
} else {
}
const transactionHash = getAccountTransactionHash(accountTransaction, signatures);
const transactionStatus = await client.getTransactionStatus(transactionHash);
getAccountInfo
Retrieves information about an account. If no account exists with the provided address, then the node
will check if any credential with that credential identifier exists and will return information
about the credential instead. If neither an account or credential matches the address at the provided
block, then undefined will be returned.
const accountAddress = new AccountAddress("3sAHwfehRNEnXk28W7A3XB3GzyBiuQkXLNRmDwDGPUe8JsoAcU");
const blockHash = "6b01f2043d5621192480f4223644ef659dd5cda1e54a78fc64ad642587c73def";
const accountInfo: AccountInfo = await client.getAccountInfo(accountAddress, blockHash);
const amount: bigint = accountInfo.accountAmount;
const nationality: string = accountInfo.accountCredentials[0].value.contents.policy.revealedAttributes["nationality"];
getNextAccountNonce
Retrieves the next account nonce, i.e. the nonce that must be set in the account transaction
header for the next transaction submitted by that account. Along with the nonce there is a boolean
that indicates whether all transactions are finalized. If this is true, then the nonce is reliable,
if not then the next nonce might be off.
const accountAddress = new AccountAddress("3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt");
const nextAccountNonce: NextAccountNonce = await client.getNextAccountNonce(accountAddress);
const nonce: bigint = nextAccountNonce.nonce;
const allFinal: boolean = nextAccountNonce.allFinal;
if (allFinal) {
}
getTransactionStatus
Retrieves status information about a transaction.
const transactionHash = "f1f5f966e36b95d5474e6b85b85c273c81bac347c38621a0d8fefe68b69a430f";
const transactionStatus: TransactionStatus = await client.getTransactionStatus(transactionHash);
const isFinalized = transactionStatus.status === TransactionStatusEnum.Finalized;
...
Note that there will be no outcomes for a transaction that has only been received:
if (transactionStatus.status === TransactionStatusEnum.Received) {
const outcomes = Object.values(transactionStatus.outcomes);
}
If the transaction has been finalized, then there is exactly one outcome:
if (transactionStatus.status === TransactionStatusEnum.Finalized) {
const outcomes = Object.values(transactionStatus.outcomes);
}
A transaction was successful if it is finalized and it has a successful outcome:
if (transactionStatus.status === TransactionStatusEnum.Finalized) {
const event = Object.values(response.outcomes)[0];
if (event.result.outcome === "success") {
}
}
getBlockSummary
Retrives a summary for a specific block. The summary contains information about finalization, the
current chain parameters, a list of the governance keys, information about any queued chain parameter
updates and a summary of any transactions within the block.
const blockHash = "7f7409679e53875567e2ae812c9fcefe90ced8761d08554756f42bf268a42749";
const blockSummary: BlockSummary = await client.getBlockSummary(blockHash);
const numberOfFinalizers = blockSummary.finalizationData.finalizers.length;
...
getBlockInfo
Retrieves information about a specific block.
const blockHash = "7f7409679e53875567e2ae812c9fcefe90ced8761d08554756f42bf268a42749";
const blockInfo: BlockInfo = await client.getBlockInfo(blockHash);
const transactionsCount = blockInfo.transactionCount;
...
getBlocksAtHeight
Retrieves the hashes of blocks at a specific height.
const blockHeight: bigint = 5310n;
const blocksAtHeight: string[] = await client.getBlocksAtHeight(blockHeight);
getConsensusStatus
Retrieves the current consensus status from the node.
const consensusStatus: ConsensusStatus = await client.getConsensusStatus();
const bestBlock = consensusStatus.bestBlock;
...
getCryptographicParameters
Retrieves the global cryptographic parameters for the blockchain at a specific block.
These are a required input for e.g. creating credentials.
const blockHash = "7f7409679e53875567e2ae812c9fcefe90ced8761d08554756f42bf268a42749"
const cryptographicParameters = await client.getCryptographicParameters(blockHash);
...
getIdentityProviders
Retrieves the list of identity providers at a specific block.
const blockHash = "7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749";
const identityProviders = await client.getIdentityProviders(blockHash);
...
getAnonymityRevokers
Retrieves the list of anonymity revokers at a specific block.
const blockHash = "7f7409679e53875567e2ae812c9fcefe90ced8961d08554756f42bf268a42749";
const identityProviders = await client.getAnonymityRevokers(blockHash);
...
getPeerList
Retrieves the list of peers that the node is connected to, including some
connection information about them. A boolean parameter determines if this
should include bootstrapper nodes or not.
const peerListResponse = await client.getPeerList(false);
const peersList = peerListResponse.getPeersList();
...
Check block for transfers with memo
The following example demonstrates how to check and parse a block
for transfers with a memo.
const blockHash = "b49bb1c06c697b7d6539c987082c5a0dc6d86d91208874517ab17da752472edf";
const blockSummary = await client.getBlockSummary(blockHash);
const transactionSummaries = blockSummary.transactionSummaries;
for (const transactionSummary of transactionSummaries) {
if (transactionSummary.result.outcome === 'success') {
if (instanceOfTransferWithMemoTransactionSummary(transactionSummary)) {
const [transferredEvent, memoEvent] = transactionSummary.result.events;
const toAddress = transferredEvent.to.address;
const amount = transferredEvent.amount;
const memo = memoEvent.memo;
}
}
}
Build
Building for a release
To build the project run
yarn build
Publishing a release
Before publishing a new release it is essential that it has been built first. So make sure that
you have just built the up-to-date code you want to publish. To publish the release run
yarn publish
and step through the steps precented to you.
Updating the gRPC files
If the external dependency concordium-grpc-api has been updated (or this is your first time building the project),
then it is required to regenerate the files from the .proto
file. Do this by running:
yarn generate
This will overwrite the existing files in src/grpc/
. Remember to check that existing functionality still
works after performing an update.
Test
An automatic test suite is part of this project, and it is run by executing:
yarn test
Note that the tests require a locally running concordium-node on the testnet. Otherwise the tests will fail.