kadena.js - Client
Core library for building Pact expressions to send to the blockchain in js. Makes use of .kadena/pactjs-generated
API Reference can be found here client.api.md
Release @kadena/client
We've created a library that allows Javascript/Typescript users to easily interact with the Kadena Blockchain. Creating contracts is explicitly left out as it's a lot more complicated to transpile Javascript to Pact.
Interaction with the Kadena Blockchain works in multiple ways. With @kadena/client there are two ways you will be able to interact with the Kadena Blockchain. The two ways are:
1. contract based, and 2. template based, and 3. modular pact command.
There's also information on an Integrated way of signing using Chainweaver. With @kadena/client you can also Send a request to the blockchain. They will be covered in this article. We will also be exploring the concepts and rationale of @kadena/client.
- kadena.js - Client
- Prerequisites
- Contract based interaction using @kadena/client
- Building a simple transaction from the contract
- Integrated sign request to Chainweaver desktop
- Template based interaction using @kadena/client
- Send a request to the blockchain
- Further development
- Contact the team
Prerequisites
Please note that we are not going over installation of nodejs and package management as this information is widely available on the internet. We will, however, provide the bare minimum of the information and details you will need in order to start using @kadena/client, which are:
- install nodejs version 14.x or 16.x
- create a directory, bootstrap a
package.json
by runningnpm init
ornpm init -y
to use defaults - install typescript
npm install -g typescript
- install the client
npm install @kadena/client
- install the commandline tool
npm install @kadena/pactjs-cli
- install the optional typescript runner for nodejs
npm install ts-node
mkdir my-dapp-with-kadena-client
cd my-dapp-with-kadena-client
npm init -y
npm install -g typescript
tsc --init
npm install --save @kadena/client
npm install -g --save-dev @kadena/pactjs-cli ts-node
Contract based interaction using @kadena/client
We wanted @kadena/client
to be independent of anything so this is just a tool
that can be used with arbitrary contracts. That is also the reason why you have
to generate the interfaces that are used by @kadena/client
. You can use the
smart-contracts from the blockchain or from your own local smart-contracts.
For the template based interaction we will provide a repository with templates that can be used.
Load contracts from the blockchain
Using the commandline tool @kadena/pactjs-cli
, download the contracts you want
to create Typescript interfaces for.
mkdir contracts
npx pactjs retrieve-contract --out "./contracts/coin.module.pact" --module "coin"
There are several options to retrieve contracts from another network or chain.
Help information on retrieve-contract (click to open)
pactjs retrieve-contract --help
pactjs retrieve-contract --help
> pactjs retrieve-contract --help
Usage: index retrieve-contract [options]
Retrieve contract from api.chainweb.com in a /local call
Options:
-m, --module <module>
The module you want to retrieve (e.g. "coin")
-o, --out <file> File to write the contract
-n, --network <network> Network to retrieve from (default "mainnet") (default:
"mainnet")
-c, --chain <number> Chain to retrieve from (default 1) (default: 1)
-h, --help display help for command
Generate interfaces
Using the contract we'll now generate all the functions (defun
s) with their
(typed) arguments and the capabilities (defcap
s).
pactjs contract-generate --file "./contracts/coin.module.pact"
The log shows what has happened. Inside the node_modules
directory, a new
package has been created: .kadena/pactjs-generated
. This package is referenced by
@kadena/client
to give you type information.
NOTE: do not forget to add this
"types": [".kadena/pactjs-generated"],
tocompilerOptions
intsconfig.json
. Otherwise this will not work
Building a simple transaction from the contract
Take a look at https://github.com/kadena-community/kadena.js/blob/master/packages/libs/pactjs-test-project/src/example-contract/simple-transfer.ts for the full example
Now that everything is bootstrapped, we can start building transactions.
Create a new file and name it transfer.ts
(or .js
).
import { Pact } from '@kadena/client';
// store the builder in a variable
const transactionBuilder =
// basic call results in Pact code `(coin.transfer "k:your-pubkey" "k:receiver-pubkey" 231.0)`
Pact.modules.coin
.transfer('k:your-pubkey', 'k:receiver-pubkey', 231)
// add necessary coin.GAS capability (this defines who pays the gas)
.addCap('coin.GAS', 'your-pubkey')
// add necessary coin.TRANSFER capability
.addCap(
'coin.TRANSFER',
'your-pubkey',
'k:your-pubkey',
'k:receiver-pubkey',
231,
)
// the minimum you NEED to add is the sender of this transaction
.setMeta({
sender: 'your-pubkey',
});
Note
Take note of the following:
- namespaced arguments (
k:
,w:
etc) are account-names, where non-namespaced arguments are assumed to be public-keys - the contract doesn't specify whether you need to pass an account-name or public-key. This is knowledge that can be obtained by inspecting the contract downloaded earlier or consulting the documentation for the contract.
-
addCap
function accepts acapability
and apublic-key
of the signer of the capability. The other arguments are defined by the contract.coin.GAS
doesn't have any arguments,coin.TRANSFER
does. -
setMeta
s object has asender
property, which is apublic-key
.
Manually signing the transaction
To sign the transaction, you can use the builder to output the command
. This
can be pasted into the SigData
of Chainweaver.
// createTransaction() will calculate hashes and finalizes the unsigned transaction
const unsignedTransaction = transactionBuilder.createCommand();
console.log(JSON.stringify(unsignedTransaction.cmd));
Take note of the following:
-
createCommand()
will finalize the transaction. The hash will be calculated. In further versions we will invalidate the hash and the command that's been generated on the transaction whenaddCap
,setMeta
or other changes are made to the transaction
Integrated sign request to Chainweaver desktop
Using the transaction
we can send a sign-request to Chainweaver. (NB: this
can only with the desktop version, not the web-version, as it's
exposing port 9467
Note
In the future we will provide an interface with WalletConnect. This is not yet finalized. Once it is, we'll update the@kadena/client
accordingly
import { signWithChainweaver } from '@kadena/client';
// use the finalized transaction, and sign it with Chainweaver
const signedTransaction = signWithChainweaver(unsignedTransaction)
.then(console.log)
.catch(console.error);
To send the transaction to the blockchain, continue with Send a request to the blockchain
Template based interaction using @kadena/client
To provide contract-developers a way to communicate how their contracts should be used, we added a way to get autocompletion for templates. Contract-developers can now provide their contracts that consumers of their smart-contract can use in Javascript.
Load the contract repository
For now we have not added a way to directly generate the code from a remote git repository. Cloning the template repository as a submodule is a great option. This gives you a way to version the source of the templates.
git submodule add \
git@github.com:kadena-community/kadena-coin-templates.git \
./templates/
Useful `git submodule` commands (click to open)
-
Add a Git repository as a submodule:
git submodule add repository_url
-
Add a Git repository as a submodule at the specified directory:
git submodule add repository_url path/to/directory
-
Update every submodule to its latest commit:
git submodule foreach git pull
-
Install a repository's specified submodules (after cloning the repo):
git submodule update --init --recursive
Generate code from templates
Usually a template directory/repository contains multiple templates, but they're all from the same source. So we're grouping them per directory/repository. This is done by selecting the directory as input for the command.
This command will result in one file containing all the templates.
pactjs template-generate --file ./templates/kadena-coin-templates/ --out ./generated/kadena-coin-templates.ts
Notes on the input (--file
) and output (--out
):
-
-f, --file
- selecting a file as input will create ONLY code for that file
- selecting a directory as input will create code for ALL the templates in the directory
-
-o, --out
- when the output is a file, the code for the templates will end up in that file
- when the output is a directory, an
index.ts
will be created in that directory, containing the code for the templates
A function is generated from a template
Each file in the repository is converted to a function that can be called. The function has one argument; an object that contains named key-value pairs for each variable in the template.
For example, a bogus template that looks like this
# ./hello.txt
This is a Hello, {{name}}!
Will have it's function call:
import myTemplates from './myTemplates';
myTemplates.hello({ name: 'alber70g' });
Of course this isn't a valid template to be used as a transaction, so this won't work. This outlines the general idea of how templates are used.
Using the generated code
Let's say we're using this template. Templates aren't valid yaml
. They are
however checked to be valid transactions when used as templates.
code: |-
(coin.transfer "{{fromAcct}}" "{{toAcct}}" {{amount}})
data:
publicMeta:
chainId: '{{chain}}'
sender: {{fromAcct}}
gasLimit: 2500
gasPrice: 1.0e-8
ttl: 600
networkId: {{network}}
signers:
- pubKey: {{fromKey}}
caps:
- name: 'coin.TRANSFER'
args: [{{fromAcct}}, {{toAcct}}, {{amount}}]
- name: 'coin.GAS'
args: []
type: exec
Each of the {{name}}
s are variables that can be passed to the template
function.
The function returns a CommandBuilder
, this can be used to add metadata to the
transaction and to signWithChainweaver(unsignedTx)
as shown here
import kadenaCoinTemplates from './templates/kadena-coin-templates';
// this returns a commandBuilder
const commandBuilder = kadenaCoinTemplates['safe-transfer']({
fromAcct: 'k:sender-pubkey',
toAcct: 'k:receiver-pubkey',
fromKey: 'sender-pubkey',
amount: '231',
chain: '1',
network: 'mainnet01',
});
const unsignedTransaction = commandBuilder.createTransaction();
Using the PactCommand class
If you don't wish to generate JS code for your contracts, or use templates, you can use the PactCommand class directly to build a command modularly, and then easily send it.
import { PactCommand } from '@kadena/client';
var pactCommandBuilder = new PactCommand()
.addData({ // Add environment data to the transaction
person: "Randy",
})
.setMeta({ // Update the metadata with a new sender and chain, and set the chain
publicMeta: {
chainId: '8',
sender: 'k:abc',
},
networkId: 'testnet04',
})
.addCap({ // Update the capabilities
signer: 'k:abc'
capability:'coin.GAS'
})
.addSignatures([{ // Add signatures
pubkey: 'k:abc',
sig: 'xyz',
}]);
pactCommandBuilder.code = '(format "Hello {}!" [(read-msg "person")])';
// Send it or local it
pactCommandBuilder.local('https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/8/pact');
pactCommandBuilder.send('https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/8/pact');
Send a request to the blockchain
The ICommandBuilder
has a few utility functions. They're taken from the
Pactjs API.
-
local
, -
send
and -
poll
.
Probably the simplest call you can make is describe-module
, but as this is not
on the coin
contract, we have to trick Typescript a little:
const res = await Pact.modules.coin['get-balance']('albert').local(
'https://api.testnet.chainweb.com/chainweb/0.0/testnet04/chain/1/pact/',
);
console.log(JSON.stringify(res, null, 2));
A more elaborate example that includes signing, sending, and polling can be found here: https://github.com/kadena-community/kadena.js/blob/master/packages/libs/pactjs-test-project/src/example-contract/transfer.ts
Further development
This is the launch post of @kadena/client
. Next steps will be to see what the
community thinks of this approach. We'd love to hear your feedback and use
cases, especially when the current @kadena/client
and @kadena/pactjs-cli
isn't sufficient.
Contact the team
We try to be available via Discord and Github issues:
- Github Issues
- Discord in the #kadena-js channel