Kadena client libraries
The Kadena client is a collection`of libraries, functions, and utilities written in TypeScript to provide a familiar application programming interface (API) for interacting with smart contracts and the Kadena network.
For a list of released Kadena client packages, see the kadena.js repository.
This section provides basic reference information for the @kadena/client
library.
The @kadena/client
library provides a TypeScript-based API for interacting
with smart contracts and Chainweb nodes on the Kadena network.
The library includes modules to help you perform the following types of common tasks:
- Create commands
- Sign transactions
- Submit transactions
- Query transaction results
Pact.modules
Use Pact.modules
to call smart contract functions with specified arguments.
import { Pact } from `@kadena/client`; Pact.modules[`${namespace}.${moduleName}`][functionName](...args);
import { Pact } from `@kadena/client`; Pact.modules[`${namespace}.${moduleName}`][functionName](...args);
Parameter | Type | Description |
---|---|---|
...args | PactValue[] | List of arguments. |
Valid PactValue types:
type PactValue = | string | number | boolean | Date | { int: string } | { decimal: string } | PactValue[] | Record<string, PactValue>;
type PactValue = | string | number | boolean | Date | { int: string } | { decimal: string } | PactValue[] | Record<string, PactValue>;
Examples
To create the code for the coin.transfer
function:
import { Pact } from `@kadena/client`; const code = Pact.modules.coin.transfer("alice", "bob", { decimal: '1.1' });// code === '(coin.transfer "alice" "bob" 1.1)'
import { Pact } from `@kadena/client`; const code = Pact.modules.coin.transfer("alice", "bob", { decimal: '1.1' });// code === '(coin.transfer "alice" "bob" 1.1)'
To create the code for the free.my-module.my-function
function that converts a list, objects, and date to valid Pact code:
import { Pact } from `@kadena/client`; const code = Pact.modules["free.my-module"].["my-function"](["first", { time: new Date() }]);// code === '(free.my-module.my-function ["first" {"time" : (time "2023-07-20T14:55:11Z")} ])'
import { Pact } from `@kadena/client`; const code = Pact.modules["free.my-module"].["my-function"](["first", { time: new Date() }]);// code === '(free.my-module.my-function ["first" {"time" : (time "2023-07-20T14:55:11Z")} ])'
Pact.builder.execution
Use Pact.builder
to create an execution (exec) command object, IPactCommand.payload.exec.code
.
Pact.builder.execution(...codes): IBuilder
Pact.builder.execution(...codes): IBuilder
Parameter | Type | Description |
---|---|---|
...codes | string[] | List of input for a function. |
Examples
To use strings for the command code:
const builder: IBuilder = Pact.builder.execution( `(coin.transfer "alice" "bob" 1.1)`,);
const builder: IBuilder = Pact.builder.execution( `(coin.transfer "alice" "bob" 1.1)`,);
To use Pact.modules
for the command code:
const builder: IBuilder = Pact.builder.execution( Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' }),);
const builder: IBuilder = Pact.builder.execution( Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' }),);
Pact.builder.continuation
Use Pact.builder
to create a continuation command object, IPactCommand.payload.cont
.
Pact.builder.continuation(contData): IBuilder
Pact.builder.continuation(contData): IBuilder
Parameter | Type | Description |
---|---|---|
contData | object | Continuation data includes a unique defpact identifier, whether the transactions rolls back a previous transaction, the transaction step that the continuation represents with the first step being step 0, and a simple payment verification proof if one is generated by calling the /spv endpoint. |
The contData
object consists of the following properties:
{ pactId: string, rollback: boolean, step: number, data?: Record<string, any>, proof?: null \| string}
{ pactId: string, rollback: boolean, step: number, data?: Record<string, any>, proof?: null \| string}
Example
The coin.cross-chain
function is a defpact
multi-step transaction that burns tokens in
the source chain and mints tokens in the destination chain.
After the first step completes successfully, you can call the second step by using the continuation
command object.
const builder: IBuilder = Pact.builder.continuation({ pactId, rollback: false, step:1, proof: spvProof})
const builder: IBuilder = Pact.builder.continuation({ pactId, rollback: false, step:1, proof: spvProof})
addSigner
Use addSigner
to add public keys and capabilities for a transaction signer to a command.
You can call addSigner
multiple times to add multiple signers to the transaction.
Pact.builder.execution(...codes).addSigner(signerOrSignersList, capabilityCallback): IBuilder
Pact.builder.execution(...codes).addSigner(signerOrSignersList, capabilityCallback): IBuilder
Parameter | Type | Description |
---|---|---|
signer | string or object | Public key of the signer or the signer object (this can also be a list of signers if all of the signers sign for the same capabilities). |
capabilityCallback | (signFor) => ReturnType[] | Allows you to scope what the signer is signing for to a specific list of capabilities. |
The signer
object consists of the following properties:
{ pubKey: string; scheme?: 'ED25519' \| 'ETH' \| 'WebAuthn'; address?: string; }
{ pubKey: string; scheme?: 'ED25519' \| 'ETH' \| 'WebAuthn'; address?: string; }
Chainweb supports the following signature schemes for public keys:
ED25519
WebAuthn
ETH
The default signature scheme is ED25519
.
You can pass just the public key if the signature scheme is ED25519
.
If the scheme is not ED25519
, you must pass a signer object that includes the pubic key and the signature scheme.
Examples
To add a signer public key for a coin
contract transfer:
// ED25519 keyconst alicePublicKey = 'e7f4da07b1d200f6e45aa6492afed6819297a97563859a5f0df9c54f5abd4aab'; Pact.builder .execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })) .addSigner(alicePublicKey, (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]);
// ED25519 keyconst alicePublicKey = 'e7f4da07b1d200f6e45aa6492afed6819297a97563859a5f0df9c54f5abd4aab'; Pact.builder .execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })) .addSigner(alicePublicKey, (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]);
To add a signer that uses the WebAuthn
scheme:
Pact.builder .execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })) .addSigner({ pubKey: webAuthnPublicKey, scheme: 'WebAuthn' }, (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]);
Pact.builder .execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })) .addSigner({ pubKey: webAuthnPublicKey, scheme: 'WebAuthn' }, (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]);
To add a list of signers with no capabilities:
Pact.builder .execution('(free.my-module.my-function)') .addSigner([ 'ED25519_publicKey', { pubKey: 'WebAuthn_publicKey', scheme: 'WebAuthn' }, ]);
Pact.builder .execution('(free.my-module.my-function)') .addSigner([ 'ED25519_publicKey', { pubKey: 'WebAuthn_publicKey', scheme: 'WebAuthn' }, ]);
To add a list of signers with similar capabilities:
Pact.builder .execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })) // e.g., Alice's account is guarded by two keys .addSigner(['first_publicKey', 'second_publicKey'], (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]); const equivalentPactCommand = { payload: { exec: { code: '(coin.transfer "alice" "bob" 1.1 )', data: {}, }, }, signers: [ { pubKey: 'first_publicKey', scheme: 'ED25519', clist: [ { name: 'coin.TRANSFER', args: ['alice', 'bob', { decimal: '1.1' }] }, ], }, { pubKey: 'second_publicKey', scheme: 'ED25519', clist: [ { name: 'coin.TRANSFER', args: ['alice', 'bob', { decimal: '1.1' }] }, ], }, ],};
Pact.builder .execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })) // e.g., Alice's account is guarded by two keys .addSigner(['first_publicKey', 'second_publicKey'], (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]); const equivalentPactCommand = { payload: { exec: { code: '(coin.transfer "alice" "bob" 1.1 )', data: {}, }, }, signers: [ { pubKey: 'first_publicKey', scheme: 'ED25519', clist: [ { name: 'coin.TRANSFER', args: ['alice', 'bob', { decimal: '1.1' }] }, ], }, { pubKey: 'second_publicKey', scheme: 'ED25519', clist: [ { name: 'coin.TRANSFER', args: ['alice', 'bob', { decimal: '1.1' }] }, ], }, ],};
addData
Use addData
to add data to the IPactCommand.payload.exec.data
or
IPactCommand.payload.cont.data
command.
This data is readable in the smart contract later.
You can also use this data in the code you set in the command.
Pact.builder .execution(...codes) .addData(key, value): IBuilder
Pact.builder .execution(...codes) .addData(key, value): IBuilder
Parameter | Type | Description |
---|---|---|
key | string | The key associated with the data you're sending. |
value | PactValue | Data that you want to send. |
Examples
To transfer with parameters in data:
Pact.builder .execution('(coin.transfer (read-string "sender") (read-string "receiver") 1.1)') .addData("sender", sender) .addData("receiver", sender): IBuilder
Pact.builder .execution('(coin.transfer (read-string "sender") (read-string "receiver") 1.1)') .addData("sender", sender) .addData("receiver", sender): IBuilder
To use transfer-create
and send the receiver guard:
Pact.builder .execution( '(coin.transfer-create "alice" "bob" (read-keyset "bob-guard") 1.1)', ) .addData('bob-guard', { keys: ['bob-public-key'], pred: 'keys-all', });
Pact.builder .execution( '(coin.transfer-create "alice" "bob" (read-keyset "bob-guard") 1.1)', ) .addData('bob-guard', { keys: ['bob-public-key'], pred: 'keys-all', });
addKeyset
Use addKeyset
as an alternative to the addData
method to add a keyset to a command.
Pact.builder .execution(...codes) .addKeyset(name, pred, ...keys): IBuilder
Pact.builder .execution(...codes) .addKeyset(name, pred, ...keys): IBuilder
Parameter | Type | Description |
---|---|---|
name | string | The name associated with the keyset. |
pred | string | One of the built-in keys-all , keys-2 , keys-any predicate functions or a user-defined predicate function. |
...keys | ...string[] | List of public keys in the keyset. |
Examples
To use readKeyset
and addKeyset
helper functions with transfer-create
:
Pact.builder .execution( Pact.modules.coin['transfer-create']( 'alice', 'bob', readKeyset('bob-guard'), { decimal: '1.1' }, ), ) .addKeyset('bob-guard', 'keys-all', 'bob-public-key');
Pact.builder .execution( Pact.modules.coin['transfer-create']( 'alice', 'bob', readKeyset('bob-guard'), { decimal: '1.1' }, ), ) .addKeyset('bob-guard', 'keys-all', 'bob-public-key');
To use transfer-create as string code:
Pact.builder .execution( '(coin.transfer-create "alice" "bob" (readKeyset "bob-guard") 1.1)', ) .addKeyset('bob-guard', 'keys-all', 'bob-public-key');
Pact.builder .execution( '(coin.transfer-create "alice" "bob" (readKeyset "bob-guard") 1.1)', ) .addKeyset('bob-guard', 'keys-all', 'bob-public-key');
setMeta
Use setMeta
to add metadata to a command.
Pact.builder .execution(...codes) .setMeta(meta): IBuilder
Pact.builder .execution(...codes) .setMeta(meta): IBuilder
Parameter | Type | Description |
---|---|---|
meta | object | Add a metadata object to the command. |
The meta
object consists of the following properties:
Property | Type | Default value | Description |
---|---|---|---|
chainId | string | undefined | Chain identifier for the chain. Valid values are from 0 to 19. |
senderAccount | string | undefined | The account name that you want to pay transaction fees from. |
gasLimit | number | 2500 | Maximum units of gas that you want to allow to be deducted when running the transaction. |
gasPrice | number | 1.0e-8 | Price of each gas unit based on KDA (e.g., 0.0000001). |
ttl | number | 28800 | Time-to-live (ttl) for the transaction to be valid in seconds. The default value is 8 hours. |
creationTime | number | Date.now() / 1000 | Transaction creation time in seconds. |
Examples
Pact.builder .execution('(coin.transfer "alice" "bob" 1.1)') // "bob is paying gas fee" .setMeta({ chainId: "02", senderAccount: "bob" }): IBuilder;
Pact.builder .execution('(coin.transfer "alice" "bob" 1.1)') // "bob is paying gas fee" .setMeta({ chainId: "02", senderAccount: "bob" }): IBuilder;
setNonce
Use setNonce
to set IPactCommand.nonce
to a custom nonce for the transaction. Otherwise, the nonce is set using the kjs:${timestamp}
function.
Pact.builder.execution(code).setNonce(nonce): IBuilder
Pact.builder.execution(code).setNonce(nonce): IBuilder
Parameter | Type | Description |
---|---|---|
nonce | string | Custom nonce for the transaction. |
Examples
Pact.builder .execution('(coin.transfer "alice" "bob" 1.1)') // "bob is paying gas fee" .setNonce("a-custom-nonce"): IBuilder;
Pact.builder .execution('(coin.transfer "alice" "bob" 1.1)') // "bob is paying gas fee" .setNonce("a-custom-nonce"): IBuilder;
setNetworkId
Use setNetworkId
to set IPactCommand.network
to specify the network for the transaction.
Pact.builder.execution(code).setNetworkId(networkId): IBuilder
Pact.builder.execution(code).setNetworkId(networkId): IBuilder
Parameter | Type | Description |
---|---|---|
networkId | string | Network identifier, for example, "mainnet01" or "testnet04". |
Examples
Pact.builder .execution('(coin.transfer "alice" "bob" 1.1)') // "bob is paying gas fee" .setNetworkId("testnet04"): IBuilder;
Pact.builder .execution('(coin.transfer "alice" "bob" 1.1)') // "bob is paying gas fee" .setNetworkId("testnet04"): IBuilder;
createTransaction
use createTransaction
to create the transaction object.
The createTransaction
method adds all of the default values to the command, converts cmd
to a string, and adds the hash.
You must add signatures to the transaction object using a wallet to submit the transaction to the blockchain.
For information about adding signatures from a wallet, see Signing transactions.
const transaction: IUnsignedCommand = Pact.builder .execution(code) .createTransaction(); // : { cmd:"stringified-command" , hash:"command-hash" , sig: [] };
const transaction: IUnsignedCommand = Pact.builder .execution(code) .createTransaction(); // : { cmd:"stringified-command" , hash:"command-hash" , sig: [] };
Examples
const transaction = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .createTransaction(); const output = { cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46\\" \\"k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11\\" 1.0)","data":{}}},"nonce":"kjs:nonce:1711376792115","signers":[{"pubKey":"dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","scheme":"ED25519","clist":[{"name":"coin.GAS","args":[]},{"name":"coin.TRANSFER","args":["k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11",{"decimal":"1"}]}]}],"meta":{"gasLimit":2500,"gasPrice":1e-8,"sender":"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","ttl":28800,"creationTime":1711376792,"chainId":"0"},"networkId":"testnet04"}', hash: 'xYePm_YgO6-T9yIlCZWzOt2s4CkZcQwqWx9Iu5tVSLI', sigs: [undefined],};
const transaction = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .createTransaction(); const output = { cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46\\" \\"k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11\\" 1.0)","data":{}}},"nonce":"kjs:nonce:1711376792115","signers":[{"pubKey":"dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","scheme":"ED25519","clist":[{"name":"coin.GAS","args":[]},{"name":"coin.TRANSFER","args":["k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11",{"decimal":"1"}]}]}],"meta":{"gasLimit":2500,"gasPrice":1e-8,"sender":"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","ttl":28800,"creationTime":1711376792,"chainId":"0"},"networkId":"testnet04"}', hash: 'xYePm_YgO6-T9yIlCZWzOt2s4CkZcQwqWx9Iu5tVSLI', sigs: [undefined],};
getCommand
Use getCommand
to use the non-stringified version of the command.
const transaction: IPactCommand = Pact.builder.execution(code).getCommand();
const transaction: IPactCommand = Pact.builder.execution(code).getCommand();
Examples
const command = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .getCommand(); const output = { payload: { exec: { code: '(coin.transfer "k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46" "k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11" 1.0)', data: {}, }, }, nonce: 'kjs:nonce:1711448853909', signers: [ { pubKey: 'dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', scheme: 'ED25519', clist: [ { name: 'coin.GAS', args: [] }, { name: 'coin.TRANSFER', args: [ 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', 'k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11', { decimal: '1' }, ], }, ], }, ], meta: { gasLimit: 2500, gasPrice: 1e-8, sender: 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', ttl: 28800, creationTime: 1711448853, chainId: '0', }, networkId: 'testnet04',};
const command = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .getCommand(); const output = { payload: { exec: { code: '(coin.transfer "k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46" "k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11" 1.0)', data: {}, }, }, nonce: 'kjs:nonce:1711448853909', signers: [ { pubKey: 'dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', scheme: 'ED25519', clist: [ { name: 'coin.GAS', args: [] }, { name: 'coin.TRANSFER', args: [ 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', 'k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11', { decimal: '1' }, ], }, ], }, ], meta: { gasLimit: 2500, gasPrice: 1e-8, sender: 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', ttl: 28800, creationTime: 1711448853, chainId: '0', }, networkId: 'testnet04',};
initialPactCommand
Use initialPactCommand
to set default values for commands you want to reuse in a custom command builder.
Parameter | Type | Description |
---|---|---|
initial | Partial | The initial Pact command values that you want to reuse. |
const builder: ITransactionBuilder = createTransactionBuilder(initialPactCommand);
const builder: ITransactionBuilder = createTransactionBuilder(initialPactCommand);
Examples
To create a transaction builder with network and chain already set:
// Pre-configure the builderexport const txBuilder = createTransactionBuilder({ networkId: "mainnet01", meta: { chainId: "1" } }); // Then somewhere in the code const command = txBuilder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ senderAccount }) const output = const output = { payload: { exec: { code: '(coin.transfer "k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46" "k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11" 1.0)', data: {}, }, }, nonce: 'kjs:nonce:1711448853909', signers: [ { pubKey: 'dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', scheme: 'ED25519', clist: [ { name: 'coin.GAS', args: [] }, { name: 'coin.TRANSFER', args: [ 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', 'k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11', { decimal: '1' }, ], }, ], }, ], meta: { gasLimit: 2500, gasPrice: 1e-8, sender: 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', ttl: 28800, creationTime: 1711448853, // Default value chainId: '1', }, // Default value networkId: 'mainnet01',};
// Pre-configure the builderexport const txBuilder = createTransactionBuilder({ networkId: "mainnet01", meta: { chainId: "1" } }); // Then somewhere in the code const command = txBuilder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ senderAccount }) const output = const output = { payload: { exec: { code: '(coin.transfer "k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46" "k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11" 1.0)', data: {}, }, }, nonce: 'kjs:nonce:1711448853909', signers: [ { pubKey: 'dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', scheme: 'ED25519', clist: [ { name: 'coin.GAS', args: [] }, { name: 'coin.TRANSFER', args: [ 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', 'k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11', { decimal: '1' }, ], }, ], }, ], meta: { gasLimit: 2500, gasPrice: 1e-8, sender: 'k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46', ttl: 28800, creationTime: 1711448853, // Default value chainId: '1', }, // Default value networkId: 'mainnet01',};
Signing transactions
After creating the command, you need to sign it using the appropriate secret keys. The signing process is usually managed with a wallet. Kadena has two protocols for signing transactions, each serving different purposes:
-
Sign API: The Sign API allows users to send their sign requests to the wallet. The wallet is then responsible for creating and signing the transaction simultaneously. With this approach, the wallet has more freedom, making it more suitable for simple transactions.
-
Quicksign: The Quicksign API is designed to give applications full control over the command, with the wallet only responsible for adding signatures. This is the recommended method if you are using the command builder from this library.
Wallets typically have their own API for communicating with applications. You can use the API provided by the wallet, or, depending on the wallet, use one of the wallet-specific wrapper functions for convenience.
Sign function interface
The sign
function can be used two ways:
If you pass a single transaction to the function, it returns the single signed (or partially signed) transaction.
If you pass a list of transactions to the function, it returns the list of signed (or partially signed) transactions.
interface ISignFunction { (transaction: IUnsignedCommand): Promise<ICommand | IUnsignedCommand>; ( transactionList: IUnsignedCommand[], ): Promise<Array<ICommand | IUnsignedCommand>>;}
interface ISignFunction { (transaction: IUnsignedCommand): Promise<ICommand | IUnsignedCommand>; ( transactionList: IUnsignedCommand[], ): Promise<Array<ICommand | IUnsignedCommand>>;}
Single Transaction
Parameter | Type | Description |
---|---|---|
tx | IUnsignedCommand | The transaction to be signed. |
List of Transactions
Parameter | Type | Description |
---|---|---|
tsList | IUnsignedCommand[] | List of the transactions to be signed. |
createSignWithChainweaver
Use createSignWithChainweaver
to sign a transaction using Chainweaver.
This function uses the quicksign
protocol and returns the actual sign function.
createSignWithChainweaver(options:{ host?: string }): ISignFunction
createSignWithChainweaver(options:{ host?: string }): ISignFunction
Parameter | Type | Description |
---|---|---|
option | { host?: string } | option including host URL default { host: 'http://127.0.0.1:9467' } |
Examples
To sign one transaction using Chainweaver:
const signWithChainweaver = createSignWithChainweaver(); const transaction = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .createTransaction(); const signedTx = signWithChainweaver(transaction);
const signWithChainweaver = createSignWithChainweaver(); const transaction = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .createTransaction(); const signedTx = signWithChainweaver(transaction);
To sign two transactions using Chainweaver:
const signWithChainweaver = createSignWithChainweaver(); const [txOneSigned, txTwoSigned] = signWithChainweaver([txOne, txTwo]);
const signWithChainweaver = createSignWithChainweaver(); const [txOneSigned, txTwoSigned] = signWithChainweaver([txOne, txTwo]);
WalletConnect
The WalletConnect protocol and helper functions are based on KIP-017.
You must use the WalletConnect
protocol .
createWalletConnectSign
Use createWalletConnectSign
to create a walletConnect
client and session before you use the helper functions to sign transactions.
This method returns the sign
function using the sign
protocol.
The return object might contain different data than what you would pass from the transaction builder because the sign
protocol lets the wallet create the transaction.
createWalletConnectSign(client, session, walletConnectChainId): (transaction: IUnsignedCommand): Promise<ICommand | IUnsignedCommand>
createWalletConnectSign(client, session, walletConnectChainId): (transaction: IUnsignedCommand): Promise<ICommand | IUnsignedCommand>
| Parameter | Type | Description |
| --------- - | ---- | ----------- |
| client | Client | The wallet-connect
client object. |
| session | SessionTypes.Struct | The wallet-connect session object. |
| networkId | string | The network identifier, for example, mainnet01
or testnet04
. The identifier can include the kadena:
prefix, for example, kadena:mainnet01
. |
Examples
const signWithWalletConnect = createWalletConnectSign( client, session, 'mainnet01',); const signedTx = signWithWalletConnect(tx);
const signWithWalletConnect = createWalletConnectSign( client, session, 'mainnet01',); const signedTx = signWithWalletConnect(tx);
createWalletConnectQuicksign
Use createWalletConnectQuicksign
to sign a transaction and return the sign
function using thequicksign
protocol.
createWalletConnectQuicksign(client, session, walletConnectChainId): ISignFunction
createWalletConnectQuicksign(client, session, walletConnectChainId): ISignFunction
Parameter | Type | Description |
---|---|---|
client | Client | The wallet-connect client object. |
session | SessionTypes.Struct | The wallet-connect session object. |
networkId | string | The network identifier, for example, mainnet01 or testnet04 . The identifier can include the kadena: prefix, for example, kadena:mainnet01 . |
Examples
const quicksignWithWalletConnect = createWalletConnectQuicksign( client, session, 'mainnet01',); const signedTx = quicksignWithWalletConnect(tx);
const quicksignWithWalletConnect = createWalletConnectQuicksign( client, session, 'mainnet01',); const signedTx = quicksignWithWalletConnect(tx);
EckoWallet
The following functions provide the sign
and quicksign
protocols for EckoWallet to return a sign
function and other properties:
const { isInstalled, isConnected, connect } = createEckoWalletSign();const { isInstalled, isConnected, connect } = createEckoWalletQuicksign();
const { isInstalled, isConnected, connect } = createEckoWalletSign();const { isInstalled, isConnected, connect } = createEckoWalletQuicksign();
isInstalled
You can use isInstalled
to check if the EckoWallet extension is installed in the browser.
isInstalled(): boolean
isInstalled(): boolean
isConnected
You can use isConnected
to check if the application is already connected to EckoWallet.
isConnected(): Promise<boolean>
isConnected(): Promise<boolean>
connect
You can use connect
to send a connection request to EckoWallet.
connect(networkId: string): Promise<boolean>
connect(networkId: string): Promise<boolean>
createEckoWalletSign
The createEckoWalletSign
function uses the sign
protocol to communicate with EckoWallet.
The return object might contain different data than what you would pass from the transaction builder because the sign
protocol lets the wallet create the transaction.
createEckoWalletSign(options:{ host?: string }): (transaction: IUnsignedCommand): Promise<ICommand | IUnsignedCommand>
createEckoWalletSign(options:{ host?: string }): (transaction: IUnsignedCommand): Promise<ICommand | IUnsignedCommand>
Examples
To sign a transaction using EckoWallet:
const signWithEckoWallet = createEckoWalletSign(); // the wallet will create the completed oneconst partialTx = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0' }) .setNetworkId(NETWORK_ID) .createTransaction(); const signedTx = signWithEckoWallet(partialTx);
const signWithEckoWallet = createEckoWalletSign(); // the wallet will create the completed oneconst partialTx = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0' }) .setNetworkId(NETWORK_ID) .createTransaction(); const signedTx = signWithEckoWallet(partialTx);
createEckoWalletQuicksign
The createEckoWalletQuicksign
function uses the quicksign
protocol to communicate with EckoWallet.
createEckoWalletQuicksign(options:{ host?: string }): ISignFunction
createEckoWalletQuicksign(options:{ host?: string }): ISignFunction
Examples
To sign one transaction using the quicksign
protocol and EckoWallet:
const quicksignWithEckoWallet = createEckoWalletQuicksign(); const tx = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .createTransaction(); const signedTx = quicksignWithEckoWallet(partialTx);
const quicksignWithEckoWallet = createEckoWalletQuicksign(); const tx = Pact.builder .execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)) .addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]) .setMeta({ chainId: '0', senderAccount }) .setNetworkId(NETWORK_ID) .createTransaction(); const signedTx = quicksignWithEckoWallet(partialTx);
To sign two transactions using the quicksign
protocol and EckoWallet:
const quicksignWithEckoWallet = createEckoWalletQuicksign(); const [txOneSigned, txTwoSigned] = quicksignWithEckoWallet([txOne, txTwo]);
const quicksignWithEckoWallet = createEckoWalletQuicksign(); const [txOneSigned, txTwoSigned] = quicksignWithEckoWallet([txOne, txTwo]);
createSignWithKeypair
Use createSignWithKeypair
to sign transactions using a secret key in a safe environment such as a server environment or CI test pipeline and return the sign
function.
The IKeyPair
interface is defined as follows:
interface IKeyPair { publicKey: string; secretKey: string;}
interface IKeyPair { publicKey: string; secretKey: string;}
createSignWithKeypair(keyOrKeys:IKeyPair | IKeyPair[]): ISignFunction
createSignWithKeypair(keyOrKeys:IKeyPair | IKeyPair[]): ISignFunction
Examples
To sign with one key pair:
const signWithKeypair = createSignWithKeypair({ publicKey, secretKey }); const signedTx = signWithKeypair(tx);
const signWithKeypair = createSignWithKeypair({ publicKey, secretKey }); const signedTx = signWithKeypair(tx);
To sign with several key pairs:
const signWithKeypair = createSignWithKeypair([firstKeyPair, secondKeyPair]); const signedTx = signWithKeypair(tx);
const signWithKeypair = createSignWithKeypair([firstKeyPair, secondKeyPair]); const signedTx = signWithKeypair(tx);
addSignatures
Use addSignatures
to add one or more signatures you have to the transaction.
All signatures you add should either include a public key, or none of them should. If the signatures don't include the public keys, then the number of signatures must match the number of signers, and the signatures are matched based on their order.
addSignatures(transaction, ...signatures): IUnsignedCommand | ICommand
addSignatures(transaction, ...signatures): IUnsignedCommand | ICommand
Parameter | Type | Description |
---|---|---|
transaction | IUnsignedCommand | The partially signed or unsigned transaction. |
...signatures | Array<{ sig: string; pubKey: string }> | Array<{ sig: string }> |
Examples
To add a signature manually with a public key:
const signedTx = addSignatures(partiallySignedTx, { sig: 'signature-str', pubKey: 'publicKey',});
const signedTx = addSignatures(partiallySignedTx, { sig: 'signature-str', pubKey: 'publicKey',});
To add a signature based on the signer order:
const signedTx = addSignatures( twoSignersTx, { sigOne: 'signature-str' }, { sigTwo: 'signature-str' },);
const signedTx = addSignatures( twoSignersTx, { sigOne: 'signature-str' }, { sigTwo: 'signature-str' },);
createClient
Use createClient
to return the IClient
interface for communicating with Chainweb nodes.
createClient( host?: string | (options: {chainId: ChainId; networkId: string}) => string, options?: { confirmationDepth?: number }): IClientinterface IClient { getStatus: (transactionDescriptors: ITransactionDescriptor[] | ITransactionDescriptor) => Promise<IPollResponse>; submit: { (transaction: ICommand): Promise<ITransactionDescriptor>; (transactionList: ICommand[]): Promise<ITransactionDescriptor[]>; } send: { (transaction: ICommand): Promise<ITransactionDescriptor>; (transactionList: ICommand[]): Promise<ITransactionDescriptor[]>; } submitOne: (transaction: ICommand) => Promise<ITransactionDescriptor>; listen: (transactionDescriptor: ITransactionDescriptor) => Promise<ICommandResult>; pollOne: (transactionDescriptor: ITransactionDescriptor) => Promise<ICommandResult>; pollStatus: (transactionDescriptors: ITransactionDescriptor[] | ITransactionDescriptor, options?: IPollOptions) => IPollRequestPromise<ICommandResult>; getPoll: (transactionDescriptors: ITransactionDescriptor[] | ITransactionDescriptor) => Promise<IPollResponse>; local: <T extends ILocalOptions>(transaction: LocalRequestBody, options?: T) => Promise<LocalResponse<T>>; dirtyRead: (transaction: IUnsignedCommand) => Promise<ICommandResult>; preflight: (transaction: ICommand | IUnsignedCommand) => Promise<ILocalCommandResult>; signatureVerification: (transaction: ICommand) => Promise<ICommandResult>; runPact: (code: string, data: Record<string, unknown>, option: INetworkOptions) => Promise<ICommandResult>; createSpv: (transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId) => Promise<string>; pollCreateSpv: (transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId, options?: IPollOptions) => Promise<string>;}
createClient( host?: string | (options: {chainId: ChainId; networkId: string}) => string, options?: { confirmationDepth?: number }): IClientinterface IClient { getStatus: (transactionDescriptors: ITransactionDescriptor[] | ITransactionDescriptor) => Promise<IPollResponse>; submit: { (transaction: ICommand): Promise<ITransactionDescriptor>; (transactionList: ICommand[]): Promise<ITransactionDescriptor[]>; } send: { (transaction: ICommand): Promise<ITransactionDescriptor>; (transactionList: ICommand[]): Promise<ITransactionDescriptor[]>; } submitOne: (transaction: ICommand) => Promise<ITransactionDescriptor>; listen: (transactionDescriptor: ITransactionDescriptor) => Promise<ICommandResult>; pollOne: (transactionDescriptor: ITransactionDescriptor) => Promise<ICommandResult>; pollStatus: (transactionDescriptors: ITransactionDescriptor[] | ITransactionDescriptor, options?: IPollOptions) => IPollRequestPromise<ICommandResult>; getPoll: (transactionDescriptors: ITransactionDescriptor[] | ITransactionDescriptor) => Promise<IPollResponse>; local: <T extends ILocalOptions>(transaction: LocalRequestBody, options?: T) => Promise<LocalResponse<T>>; dirtyRead: (transaction: IUnsignedCommand) => Promise<ICommandResult>; preflight: (transaction: ICommand | IUnsignedCommand) => Promise<ILocalCommandResult>; signatureVerification: (transaction: ICommand) => Promise<ICommandResult>; runPact: (code: string, data: Record<string, unknown>, option: INetworkOptions) => Promise<ICommandResult>; createSpv: (transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId) => Promise<string>; pollCreateSpv: (transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId, options?: IPollOptions) => Promise<string>;}
You can use object destructuring to extract specific functions. For example:
const { submit, local, pollCreateSpv } = createClient();
const { submit, local, pollCreateSpv } = createClient();
Parameter | Type | Description |
---|---|---|
host | string | (options: {chainId: ChainId; networkId: string}) => string |
options | { confirmationDepth?: number } | Additional options for the client. It has only one property now: confirmationDepth , which can be used in the poll endpoint. Default value is 0 . |
Both host
and options
are optional arguments.
The default value of host
is a URL generator function that returns the Chainweb node URLs for mainnet
and testnet
.
If you want to use different URLs, you must specify the host
parameter.
The networkId
and chainId
parameters are read from the command object and passed to the URL generator function.
Examples
To create a client for the development network and a specific chain identifier (1):
const client = createClient("http://127.0.0.1:8080/chainweb/0.0/development/chain/1/pact");
const client = createClient("http://127.0.0.1:8080/chainweb/0.0/development/chain/1/pact");
To create a client for the development network that covers multi-chain and uses the URL generator function for more flexibility:
const devNetClient = createClient(({chainId, networkId})=> `http://127.0.0.1:8080/chainweb/0.0/${networkId}/chain/${chainId ?? '1'}/pact`);
const devNetClient = createClient(({chainId, networkId})=> `http://127.0.0.1:8080/chainweb/0.0/${networkId}/chain/${chainId ?? '1'}/pact`);
To create a client that uses mainnet
but not Kadena main network nodes:
const client = createClient(({ chainId, networkId }) => { switch (networkId) { case 'mainnet01': return `http://my-node-url/chainweb/0.0/${networkId}/chain/{${chainId}}/pact`; case 'testnet04': return `http://my-test-node-url/chainweb/0.0/${networkId}/chain/{${chainId}}/pact`; default: throw new Error('UNKNOWN_NETWORK'); }});
const client = createClient(({ chainId, networkId }) => { switch (networkId) { case 'mainnet01': return `http://my-node-url/chainweb/0.0/${networkId}/chain/{${chainId}}/pact`; case 'testnet04': return `http://my-test-node-url/chainweb/0.0/${networkId}/chain/{${chainId}}/pact`; default: throw new Error('UNKNOWN_NETWORK'); }});
To create a client with a confirmationDepth
of 5
that waits for five new blocks to be added to the chain before reading the result of a transaction:
const { submit, pollStatus } = createClient(undefined, { confirmationDepth: 5 });
const { submit, pollStatus } = createClient(undefined, { confirmationDepth: 5 });
submit
Use submit
to submit one transaction or a list of transactions to the blockchain.
To send a single transaction:
const { submit } = createClient();submit(tx): Promise<ITransactionDescriptor>;interface ITransactionDescriptor { networkId: string; chainId: ChainId; requestKey: string}
const { submit } = createClient();submit(tx): Promise<ITransactionDescriptor>;interface ITransactionDescriptor { networkId: string; chainId: ChainId; requestKey: string}
Parameter | Type | Description |
---|---|---|
tx | ICommand | The command object ready to submit. |
To submit a list of transactions:
const { submit } = createClient();submit(txList): Promise<ITransactionDescriptor[]>;
const { submit } = createClient();submit(txList): Promise<ITransactionDescriptor[]>;
Parameter | Type | Description |
---|---|---|
txList | ICommand[] | List of command objects ready to submit. |
In most cases, you should store the result of this function so you can fetch the result of the request.
The submitOne
function is the same as submitting one transaction using the submit
function.
For example:
const { submitOne } = createClient();submitOne(tx): Promise<ITransactionDescriptor>;
const { submitOne } = createClient();submitOne(tx): Promise<ITransactionDescriptor>;
Parameter | Type | Description |
---|---|---|
tx | ICommand | The command object ready to submit. |
getStatus
Use getStatus to call the /poll
endpoint and return the result of requests.
const { getStatus } = createClient();getStatus(transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor): Promise<{ [requestKey: IBase64Url]: { [requestKey:string] ICommandResult};}>
const { getStatus } = createClient();getStatus(transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor): Promise<{ [requestKey: IBase64Url]: { [requestKey:string] ICommandResult};}>
Parameters
Parameter | Type | Description |
---|---|---|
transactionDescriptor | TransactionDescriptor | TransactionDescriptor[] |
Return value
ICommandResult
interface:
interface ICommandResult { reqKey: string; txId: number | null; result: | { status: 'success'; data: PactValue; } | { status: 'failure'; error: object; }; gas: number; logs: string | null; // for defpact functions continuation: null | { pactId: PactTransactionHash; step: Step; stepCount: number; executed: boolean | null; stepHasRollback: boolean; continuation: { def: string; args: PactValue; }; yield: { data: Array<[string, PactValue]>; provenance: { targetChainId: ChainId; moduleHash: string; } | null; }; }; metaData: null | { blockHash: string; blockTime: number; blockHeight: number; prevBlockHash: string; publicMeta?: IPactCommand['meta'] }; events: Array<{ name: string; module: { name: string; namespace: string | null; }; params: Array<PactValue>; moduleHash: string; }>;}
interface ICommandResult { reqKey: string; txId: number | null; result: | { status: 'success'; data: PactValue; } | { status: 'failure'; error: object; }; gas: number; logs: string | null; // for defpact functions continuation: null | { pactId: PactTransactionHash; step: Step; stepCount: number; executed: boolean | null; stepHasRollback: boolean; continuation: { def: string; args: PactValue; }; yield: { data: Array<[string, PactValue]>; provenance: { targetChainId: ChainId; moduleHash: string; } | null; }; }; metaData: null | { blockHash: string; blockTime: number; blockHeight: number; prevBlockHash: string; publicMeta?: IPactCommand['meta'] }; events: Array<{ name: string; module: { name: string; namespace: string | null; }; params: Array<PactValue>; moduleHash: string; }>;}
pollStatus
Use pollStatus to call the /poll
endpoint in intervals and return the result of all requests when all are ready.
const { pollStatus } = createClient();pollStatus( transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor, pollOptions: { onPoll?: (id: string) => void; timeout?: Milliseconds; interval?: Milliseconds; confirmationDepth?: number; }): IPollRequestPromise<{ [requestKey: IBase64Url]: { [requestKey:string] ICommandResult};}>interface IPollRequestPromise extends Promise { [requestKey: IBase64Url]: Promise<ICommandResult>}
const { pollStatus } = createClient();pollStatus( transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor, pollOptions: { onPoll?: (id: string) => void; timeout?: Milliseconds; interval?: Milliseconds; confirmationDepth?: number; }): IPollRequestPromise<{ [requestKey: IBase64Url]: { [requestKey:string] ICommandResult};}>interface IPollRequestPromise extends Promise { [requestKey: IBase64Url]: Promise<ICommandResult>}
Parameters
Parameter | Type | Description |
---|---|---|
transactionDescriptor | TransactionDescriptor | TransactionDescriptor[] |
pollOptions | { onPoll?: (id: string) => void; timeout?: Milliseconds; interval?: Milliseconds; confirmationDepth?: number; } | onPoll: Callback is called when the request is polling; this might be called several times if the request is not ready yet. Timeout: Timeout if the result is not ready (default 180000 // 3 minutes). Interval: Delay between retries (default is 5000 // 5 seconds). ConfirmationDepth: Set the confirmationDepth for getting the response; this overrides the one you set in createClient function |
Return value
The return value is a special type of promise. Though you can
just await for the result just like a normal promise - which is the case for
most of the typical use cases - you can still listen for each individual request
via the requests
property.
Examples
Poll the status of a request:
const result = await pollStatus(request, {});
const result = await pollStatus(request, {});
Poll the status of several requests and get the result for each one immediately:
const resultPromise = pollStatus([firstRequest, secondRequest, thirdRequest]);// Notify the UI from the result of each request as soon as it's availableresultPromise.requests["first-request-key"].then(res => {UI.notify(res)});resultPromise.requests["second-request-key"].then(res => {UI.notify(res)});resultPromise.requests["third-request-key"].then(res => {UI.notify(res)});// The final result objectconst finalResult = await resultPromise;
const resultPromise = pollStatus([firstRequest, secondRequest, thirdRequest]);// Notify the UI from the result of each request as soon as it's availableresultPromise.requests["first-request-key"].then(res => {UI.notify(res)});resultPromise.requests["second-request-key"].then(res => {UI.notify(res)});resultPromise.requests["third-request-key"].then(res => {UI.notify(res)});// The final result objectconst finalResult = await resultPromise;
listen
Use listen to call the /listen
endpoint, which is a blocking endpoint.
If your network or firewall configuration doesn't allow keeping HTTP connections open for a
long time, then it's better to use pollOne
which has the same interface but
uses /poll
under the hood.
const { listen } = createClient();listen(transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor): Promise<ICommandResult>
const { listen } = createClient();listen(transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor): Promise<ICommandResult>
Parameter | Type | Description |
---|---|---|
transactionDescriptor | TransactionDescriptor | The request object including requestKet , networkId , chainId |
pollOne
Use pollOne
to call the /poll
endpoint to return the result of only one request.
const { pollOne } = createClient();pollOne(transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor): Promise<ICommandResult>
const { pollOne } = createClient();pollOne(transactionDescriptor: TransactionDescriptor[] | ITransactionDescriptor): Promise<ICommandResult>
Parameters
Parameter | Type | Description |
---|---|---|
transactionDescriptor | TransactionDescriptor | The request object including requestKet , networkId , chainId |
local
Use local
to call the /local
endpoint.
local( transaction: ICommand | IUnsignedCommand, options?: { preflight?: boolean; signatureVerification?: boolean; }): Promise<ICommandResult & { preflightWarnings?: string[] }>;
local( transaction: ICommand | IUnsignedCommand, options?: { preflight?: boolean; signatureVerification?: boolean; }): Promise<ICommandResult & { preflightWarnings?: string[] }>;
The return type is ICommandResult
with preflightWarnings
when it is set to
true.
Parameter | Type | Description |
---|---|---|
transaction | ICommand | IUnsignedCommand |
option | { preflight?: boolean; signatureVerification?: boolean; } | preflight: Runs the code in the preflight mode which simulates submitting the transaction so you can also have the gas consumption result (default = true ). SignatureVerification: Run the signature verification in the node as well; then the transaction should have the transactions as well (default = true ). |
Examples
To avoid submitting an incorrect transaction:
// Check if the transaction and signatures are correctconst response = await client.local(signedTx);if (response.result.status === 'failure') { // Throw if the transaction fails to avoid paying gas for a failed transaction throw response.result.error;}const request = await client.submit(signedTx);
// Check if the transaction and signatures are correctconst response = await client.local(signedTx);if (response.result.status === 'failure') { // Throw if the transaction fails to avoid paying gas for a failed transaction throw response.result.error;}const request = await client.submit(signedTx);
To simulate a transaction for gas estimation:
// We don't need to send signatures to check gas estimation;const response = await client.local(unsignedTx, { preflight:true , signatureVerification: false });if (response.result.status === 'failure') { throw response.result.error;}const gasEstimation = response.gas;
// We don't need to send signatures to check gas estimation;const response = await client.local(unsignedTx, { preflight:true , signatureVerification: false });if (response.result.status === 'failure') { throw response.result.error;}const gasEstimation = response.gas;
dirtyRead
Use dirtyRead
to read data from a node without submitting a transaction to the blockchain.
dirtyRead(transaction: ICommand | IUnsignedCommand): Promise<ICommandResult>;
dirtyRead(transaction: ICommand | IUnsignedCommand): Promise<ICommandResult>;
Parameters
Parameter | Type | Description |
---|---|---|
transaction | ICommand | IUnsignedCommand |
Examples
To get an account balance:
const tr = Pact.builder .execution(Pact.modules.coin['get-balance'](account)) .setMeta({ chainId: '0' }) .setNetworkId("mainnet04") .createTransaction();// We don't need to submit a transaction for just reading data,// so instead we just read the value from the local data of the blockchain nodeconst res = await dirtyRead(tr);if (res.result.status === 'failure') { throw res.result.error;}const balance = res.result.data;
const tr = Pact.builder .execution(Pact.modules.coin['get-balance'](account)) .setMeta({ chainId: '0' }) .setNetworkId("mainnet04") .createTransaction();// We don't need to submit a transaction for just reading data,// so instead we just read the value from the local data of the blockchain nodeconst res = await dirtyRead(tr);if (res.result.status === 'failure') { throw res.result.error;}const balance = res.result.data;
preflight
Use preflight
to call the /local
endpoint with the preflight
value set to true, but signatureVerification
value set to false.
preflight(transaction: ICommand | IUnsignedCommand): Promise<ICommandResult>;
preflight(transaction: ICommand | IUnsignedCommand): Promise<ICommandResult>;
Parameters
Parameter | Type | Description |
---|---|---|
transaction | ICommand | IUnsignedCommand |
signatureVerification
Use signatureVerification
to call the /local
endpoint with the signatureVerification
value set to true, but preflight
value set to false.
signatureVerification(transaction: ICommand | IUnsignedCommand): Promise<ICommandResult & { preflightWarnings?: string[] }>;
signatureVerification(transaction: ICommand | IUnsignedCommand): Promise<ICommandResult & { preflightWarnings?: string[] }>;
Parameters
Parameter | Type | Description |
---|---|---|
transaction | ICommand | IUnsignedCommand |
runPact
If you just want to see the result of a pact code and don't want to create a
command object, you can use the runPact
function. This function creates a
command object internally.
runPact(code: string, data?: Record<string, unknown>, options?: { chainId: ChainId; networkId: string }): Promise<ICommandResult>;
runPact(code: string, data?: Record<string, unknown>, options?: { chainId: ChainId; networkId: string }): Promise<ICommandResult>;
Parameters
Parameter | Type | Description |
---|---|---|
code | string | Pact code. |
data | Record<string, unknown> | Data to be sent with the transaction. |
options | { chainId: ChainId; networkId: string } | ChainId and networkId that you want to send the transaction to. |
Examples
const { runPact } = createClient()const result = await runPact(`(coin.getBalance "alice")`, { }, { networkId:"mainnet01", chainId:"1" })
const { runPact } = createClient()const result = await runPact(`(coin.getBalance "alice")`, { }, { networkId:"mainnet01", chainId:"1" })
createSPV
Use createSPV
to call the /spv
endpoint to request a simple payment verification proof if it's ready.
createSpv(transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId): Promise<string>;
createSpv(transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId): Promise<string>;
Parameters
Parameter | Type | Description |
---|---|---|
transactionDescriptor | { requestKey: string; networkId: string; chainId: ChainId } | The transaction for which you want to create an SPV proof. |
targetChainId | ChainId | The chain that consumes this proof. |
pollCreateSPV
Use pollCreateSPV
to poll the /spv
endpoint for a simple payment verification proof until the proof is ready.
pollCreateSpv( transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId, pollOptions?: { onPoll?: (id: string) => void; timeout?: Milliseconds; interval?: Milliseconds; }): Promise<string>;
pollCreateSpv( transactionDescriptor: ITransactionDescriptor, targetChainId: ChainId, pollOptions?: { onPoll?: (id: string) => void; timeout?: Milliseconds; interval?: Milliseconds; }): Promise<string>;
Parameters
Parameter | Type | Description |
---|---|---|
transactionDescriptor | { requestKey: string; networkId: string; chainId: ChainId } | The transaction for which you want to create an SPV proof. |
targetChainId | ChainId | The chain that consumes this proof. |
pollOptions | { onPoll?: (id: string) => void; timeout?: Milliseconds; interval?: Milliseconds; } | onPoll: Callback is called when the request is polling; this might be called several times if the request is not ready yet. Timeout: Timeout if the result is not ready (default 180000 // 3 minutes). Interval: Delay between retries (default is 5000 // 5 seconds) |
Examples
const request = await submit(crossChainTx)const response = await pollOne(request)// create spv proof for the transactionconst spvProof = await pollSpvProof(request)const continuationTx = Pact.builder.continuation({ pactId: response.continuation.pactId, rollback: false, step:1, proof: spvProof}).addMeta({ chainId: targetChainId, // using gas station for paying gas fee senderAccount : 'kadena-xchain-gas'}).createTransaction()const contRequest = await submit(continuationTx)const finalResult = await pollOne(contRequest)
const request = await submit(crossChainTx)const response = await pollOne(request)// create spv proof for the transactionconst spvProof = await pollSpvProof(request)const continuationTx = Pact.builder.continuation({ pactId: response.continuation.pactId, rollback: false, step:1, proof: spvProof}).addMeta({ chainId: targetChainId, // using gas station for paying gas fee senderAccount : 'kadena-xchain-gas'}).createTransaction()const contRequest = await submit(continuationTx)const finalResult = await pollOne(contRequest)
composePactCommand
Use composePactCommand
to use the functional programming API to compose parts of a Pact command and create the final command object.
type CommandReducer = (cmd?: IPartialPactCommand | (() => IPartialPactCommand)) => IPartialPactCommand; composePactCommand( ...reducersOrPartialCommands: Array<IPartialPactCommand | CommandReducer> ): CommandReducer
type CommandReducer = (cmd?: IPartialPactCommand | (() => IPartialPactCommand)) => IPartialPactCommand; composePactCommand( ...reducersOrPartialCommands: Array<IPartialPactCommand | CommandReducer> ): CommandReducer
Parameters
Parameter | Type | Description |
---|---|---|
...reducersOrPartialCommands | Array<IPartialPactCommand | CommandReducer> |
Return value
The return value is a CommandReducer
function that you can pass
to another composePactCommand.
Eventually, when you call the function, it also adds the default values.
Examples
const pactCommand = composePactCommand( { payload: { exec: { code: '(+ 1 1)' } } }, (cmd) => ({ ...cmd, meta: { chainId: '1' } }), { networkId: 'testnet04' },)(); const pactCommand = { payload: { exec: { code: '(+ 1 1)' } }, meta: { gasLimit: 2500, gasPrice: 1e-8, sender: '', ttl: 28800, creationTime: 1690416000, chainId: '1', }, networkId: 'testnet04', nonce: 'kjs:nonce:1690416000000', signers: [],};
const pactCommand = composePactCommand( { payload: { exec: { code: '(+ 1 1)' } } }, (cmd) => ({ ...cmd, meta: { chainId: '1' } }), { networkId: 'testnet04' },)(); const pactCommand = { payload: { exec: { code: '(+ 1 1)' } }, meta: { gasLimit: 2500, gasPrice: 1e-8, sender: '', ttl: 28800, creationTime: 1690416000, chainId: '1', }, networkId: 'testnet04', nonce: 'kjs:nonce:1690416000000', signers: [],};
Supported functions
You can use the following functions with composePactCommand
:
execution
continuation
addSigner
addData
addKeyset
setMeta
setNonce
setNetworkId
createTransaction
Examples
The following examples illustrate using composePactCommand in different scenarios.
Create an execution
command using Pact.modules
:
const command: IPactCommand = composePactCommand( execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })),)();
const command: IPactCommand = composePactCommand( execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })),)();
Create a continuation
command:
const command: IPactCommand = composePactCommand( continuation({ pactId, rollback: false, step:1, proof: spvProof }))()
const command: IPactCommand = composePactCommand( continuation({ pactId, rollback: false, step:1, proof: spvProof }))()
Add a signer with a key that uses the WebAuthn
signature scheme:
composePactCommand( execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })), addSigner({ pubKey: webAuthnPublicKey, scheme: 'WebAuthn' }, (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]),);
composePactCommand( execution(Pact.modules.coin.transfer('alice', 'bob', { decimal: '1.1' })), addSigner({ pubKey: webAuthnPublicKey, scheme: 'WebAuthn' }, (signFor) => [ signFor('coin.TRANSFER', 'alice', 'bob', { decimal: '1.1' }), ]),);
Add sender and receiver data to a transfer operation:
composePactCommand( execution( '(coin.transfer (read-string "sender") (read-string "receiver") 1.1)', ), addData('sender', sender), addData('receiver', sender),);
composePactCommand( execution( '(coin.transfer (read-string "sender") (read-string "receiver") 1.1)', ), addData('sender', sender), addData('receiver', sender),);
Add a keyset for a transfer and create operation:
composePactCommand( execution( Pact.modules.coin['transfer-create']( 'alice', 'bob', readKeyset('bob-guard'), { decimal: '1.1' }, ), ), addKeyset('bob-guard', 'keys-all', 'bob-public-key'),);
composePactCommand( execution( Pact.modules.coin['transfer-create']( 'alice', 'bob', readKeyset('bob-guard'), { decimal: '1.1' }, ), ), addKeyset('bob-guard', 'keys-all', 'bob-public-key'),);
Set metadata for a transfer operation:
composePactCommand( execution('(coin.transfer "alice" "bob" 1.1)'), // "bob is paying gas fee" setMeta({ chainId: '02', senderAccount: 'bob' }),);
composePactCommand( execution('(coin.transfer "alice" "bob" 1.1)'), // "bob is paying gas fee" setMeta({ chainId: '02', senderAccount: 'bob' }),);
Set a custom nonce:
composePactCommand( execution('(coin.transfer "alice" "bob" 1.1)'), // "bob is paying gas fee" setNonce('a-custom-nonce'),);
composePactCommand( execution('(coin.transfer "alice" "bob" 1.1)'), // "bob is paying gas fee" setNonce('a-custom-nonce'),);
Set the network identifier for the transaction
composePactCommand( execution('(coin.transfer "alice" "bob" 1.1)'), // "bob is paying gas fee" setNetworkId('testnet04'),);
composePactCommand( execution('(coin.transfer "alice" "bob" 1.1)'), // "bob is paying gas fee" setNetworkId('testnet04'),);
Create the transaction object to be signed:
const pactCommand = composePactCommand( execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)), addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]), setMeta({ chainId: '0', senderAccount }), setNetworkId(NETWORK_ID), createTransaction(),); const transaction = createTransaction(pactCommand); const output = { cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46\\" \\"k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11\\" 1.0)","data":{}}},"nonce":"kjs:nonce:1711376792115","signers":[{"pubKey":"dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","scheme":"ED25519","clist":[{"name":"coin.GAS","args":[]},{"name":"coin.TRANSFER","args":["k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11",{"decimal":"1"}]}]}],"meta":{"gasLimit":2500,"gasPrice":1e-8,"sender":"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","ttl":28800,"creationTime":1711376792,"chainId":"0"},"networkId":"testnet04"}', hash: 'xYePm_YgO6-T9yIlCZWzOt2s4CkZcQwqWx9Iu5tVSLI', sigs: [undefined],};
const pactCommand = composePactCommand( execution(Pact.modules.coin.transfer(senderAccount, receiverAccount, amount)), addSigner(senderKey, (signFor) => [ signFor('coin.GAS'), signFor('coin.TRANSFER', senderAccount, receiverAccount, amount), ]), setMeta({ chainId: '0', senderAccount }), setNetworkId(NETWORK_ID), createTransaction(),); const transaction = createTransaction(pactCommand); const output = { cmd: '{"payload":{"exec":{"code":"(coin.transfer \\"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46\\" \\"k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11\\" 1.0)","data":{}}},"nonce":"kjs:nonce:1711376792115","signers":[{"pubKey":"dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","scheme":"ED25519","clist":[{"name":"coin.GAS","args":[]},{"name":"coin.TRANSFER","args":["k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","k:2f48080efe54e6eb670487f664bcaac7684b4ebfcfc8a3330ef080c9c97f7e11",{"decimal":"1"}]}]}],"meta":{"gasLimit":2500,"gasPrice":1e-8,"sender":"k:dc20ab800b0420be9b1075c97e80b104b073b0405b5e2b78afd29dd74aaf5e46","ttl":28800,"creationTime":1711376792,"chainId":"0"},"networkId":"testnet04"}', hash: 'xYePm_YgO6-T9yIlCZWzOt2s4CkZcQwqWx9Iu5tVSLI', sigs: [undefined],};
Runnable examples
For samples of runnable code, see the client-examples.