Getting Started (~v1.4.1)

Prerequisites

Dependencies

The following packages are required to use the caver-js library.

Note caver-js can run on Node.js versions 8 and 10, and the recommended versions are:

If you are already using a different version of the Node (for example, Node v12), use the Node Version Manager(NVM) to install and use the version supported by caver-js.

Installation

To try it out, install caver-js with npm using the following command:

$ npm install caver-js

Note: package.json file should exist on the same install path. If it does not exist, package.json should be generated via npm init.

To install a specific version of caver-js, try the following command:

$ npm install caver-js@X.X.X

Starting with caver-js

Once you have finished installing caver-js, you can now connect caver-js with a Klaytn Node.

You can import the caver-js module and connect it to a Klaytn Node in the Baobab testnet as shown in the example below:

$ node
> const Caver = require('caver-js')
> const caver = new Caver('https://public-en-baobab.klaytn.net/')

If you are running an EN, you can connect it to your own node by changing the host and port like below:

$ node
> const Caver = require('caver-js')
> const caver = new Caver('https://your.en.url:8651/')

Managing Accounts

Creating an Account

You can use caver-js to create an account as shown below. You can also create an account via Klaytn Wallet.

> const account = caver.klay.accounts.create()

> account
{ address: '0x3bd32d55e64d6cbe54bec4f5200e678ee8d1a990',
  privateKey: '0x{private key}',
  ... }

Note: Functions associated with caver.klay.accounts have no effect on the actual Klaytn network.

Add Accounts to caver-js

You can use your account easily by using the in-memory wallet provided by caver-js. The following examples illustrate how to add an account to a wallet using an account object and a keystore file generated by Klaytn Wallet.

// Using an account object
> caver.klay.accounts.wallet.add(caver.klay.accounts.create())
{ 
    address: '0xebec0df19ed2f8b4070dec94d55a69077c544403',
    privateKey: '0x{private key}',
    signTransaction: [Function: signTransaction],
    sign: [Function: sign],
    encrypt: [Function: encrypt],
    getKlaytnWalletKey: [Function: getKlaytnWalletKey],
    index: 0 
}

// Using a keystore file.
> const decryptedAccount = caver.klay.accounts.decrypt({
        "version": 3,
        "id": "7c05d545-85ce-46c9-b6e9-9110d6597931",
        "address": "0x460406d822b5908504353deabc890e0de61eb42b",
        "crypto": {
            "ciphertext": "d2a84b99312215ca2d8ea43b251dc94fd55c3fc4a0c283538ef114d20251bf4a",
            "cipherparams": {
                "iv": "21b4298e33b8a61549f6abbecbd4d347"
            },
            "cipher": "aes-128-ctr",
            "kdf": "scrypt",
            "kdfparams": {
                "dklen": 32,
                "salt": "6c7a1618ee5525b10ddbcf0f0879214200984f583faf55af5dd2a7a0b7a58fd6",
                "n": 4096,
                "r": 8,
                "p": 1
            },
            "mac": "99e4c25ac8acf1571d4161f2c40db92a391aefd42ec871e23601a7af446432a7"
        }
    }, 'password')
> caver.klay.accounts.wallet.add(decryptedAccount)
{ 
    address: '0x460406d822b5908504353deabc890e0de61eb42b',
    privateKey: '0x{private key}',
    signTransaction: [Function: signTransaction],
    sign: [Function: sign],
    encrypt: [Function: encrypt],
    getKlaytnWalletKey: [Function: getKlaytnWalletKey] 
}

The account added to the caver-js wallet can be used for sendTransaction.

Sending a Transaction

This section will show you how to send a KLAY using caver-js on the Baobab network.

Getting KLAY via Baobab Faucet

If you need KLAY for testing, you can get Baobab testnet KLAY from the Klaytn Wallet. Log in to the Klaytn Wallet using the private key or the keystore file and receive Baobab testnet KLAY via the faucet for testing.

Sending a Value Transfer Transaction

You can use a caver-js wallet to generate a signature of a transaction. If you have an account in the caver-js wallet, the signature generation will be done with the private key inside the caver-js wallet when you execute caver.klay.sendTransaction. Note that caver.klay.sendTransaction performs both signature generation and submission of the transaction at once.

// If you have not added an account to caver-js's wallet, add it to your wallet by running 'caver.klay.accounts.wallet.add'.
// If the same account is already in the wallet, 'Error: Account exists with {hex in address}' is returned. In this case, you can use the address string in the `from` field to reference the account in the wallet.

> const account = caver.klay.accounts.wallet.add('0x{private key}')

> caver.klay.sendTransaction({
    type: 'VALUE_TRANSFER',
    from: account.address',
    to: '0xeF5cd886C7f8d85fbe8023291761341aCBb4DA01',
    gas: '300000',
    value: 1,
  }).then(console.log)
{ 
    blockHash: '0x5e9f427c9550a6f7575bcf60aba9257634884519a6273a23e8eefee2a696cce4',
    blockNumber: 3841096,
    contractAddress: null,
    from: '0x3bd32d55e64d6cbe54bec4f5200e678ee8d1a990',
    ...
    status: true,
    to: '0xef5cd886c7f8d85fbe8023291761341acbb4da01',
    transactionHash: '0xb09f6d26734074a259f6cbe4d509d2bf40f6f0a4559081354527ae211dd9d00f',
    transactionIndex: 1,
    type: 'TxTypeValueTransfer',
    typeInt: 8,
    value: '0x1' 
}

If you want to generate a signature directly from the private key without a caver-js wallet, the following steps are required:

  1. caver.klay.accounts.signTransaction - The process of signing a transaction with a private key and getting a RLP-encoded transaction.

  2. caver.klay.sendSignedTransaction - sends the RLP-encoded transaction to the node connected to caver-js.

First, to sign the transaction, specify the sender, recipient, and the private key appropriately like shown below:

Note: The sender should have enough amount of KLAY.

> caver.klay.accounts.signTransaction({
    type: 'VALUE_TRANSFER',
    from: '0x71959675eeb7c7ec1e0c74f206a9c488d7f178d4',
    to: '0xeF5cd886C7f8d85fbe8023291761341aCBb4DA01',
    gas: '300000',
    value: caver.utils.toPeb('1', 'KLAY'),
  }, '0x{private key}').then((result)=>{
      rawTransaction = result.rawTransaction
  })

You can get a RLP-encoded transaction (rawTransaction) using caver.klay.accounts.signTransaction as above and use this to transfer the transaction to the Klaytn network as below.

> caver.klay.sendSignedTransaction(rawTransaction).on('transactionHash', console.log)
0xac418c96f7386a3343d149eeb29e48e28905525dda2e5afe55b0661f9ab01aca

As shown in the example above, you can send a request and use the event emitter to get the hash of the submitted transaction by calling .on('transactionHash', console.log).

Checking Receipts

You can use the promise or event emitter to get the receipt of the transaction when you transfer the transaction to caver.klay.sendSignedTransaction or caver.klay.sendTransaction.

The following example shows how to get a receipt using promise and event emitter.

// Using promise
> caver.klay.sendSignedTransaction(rawTransaction).then(console.log)
{ 
    blockHash: '0x6ccef34eb59fab927705d344f080f449b576c0626e4aa3e20f569feb8df6e283',
    blockNumber: 19097,
    contractAddress: null,
    from: '0x71959675eeb7c7ec1e0c74f206a9c488d7f178d4',
    ...
    status: true,
    to: '0xef5cd886c7f8d85fbe8023291761341acbb4da01',
    transactionHash: '0xac418c96f7386a3343d149eeb29e48e28905525dda2e5afe55b0661f9ab01aca',
    transactionIndex: 0,
    type: 'TxTypeValueTransfer',
    typeInt: 8,
    value: '0xde0b6b3a7640000' 
}

// Using event emitter
> caver.klay.sendSignedTransaction(rawTransaction).on('receipt', console.log)
{ 
    blockHash: '0x6ccef34eb59fab927705d344f080f449b576c0626e4aa3e20f569feb8df6e283',
    blockNumber: 19097,
    contractAddress: null,
    from: '0x71959675eeb7c7ec1e0c74f206a9c488d7f178d4',
    ...
    status: true,
    to: '0xef5cd886c7f8d85fbe8023291761341acbb4da01',
    transactionHash: '0xac418c96f7386a3343d149eeb29e48e28905525dda2e5afe55b0661f9ab01aca',
    transactionIndex: 0,
    type: 'TxTypeValueTransfer',
    typeInt: 8,
    value: '0xde0b6b3a7640000' 
}

As described in the example above, you can get the result of sending a transaction through the promise and event emitter. And also, if you know the transaction hash, you can query the transaction receipt using the caver.klay.getTransactionReceipt RPC call. The example below shows how to get a receipt using the caver.klay.getTransactionReceipt RPC call.

> caver.klay.getTransactionReceipt('0xbad4dd6d80beda6c04d90f1db7e4179557ab48423d4f14295b33e38a9418e59f').then(console.log)
{ 
    blockHash: '0xd56ac90d552f924f228683f78854c0ffd9f29498f985892f726326a860378a53',
    blockNumber: 3827075,
    contractAddress: null,
    from: '0x3bd32d55e64d6cbe54bec4f5200e678ee8d1a990',
    gas: '0x493e0',
    gasPrice: '0x5d21dba00',
    gasUsed: 21000,
    logs: [],
    logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
    nonce: '0x2',
    senderTxHash: '0xbad4dd6d80beda6c04d90f1db7e4179557ab48423d4f14295b33e38a9418e59f',
    signatures: [ 
        { 
            V: '0x7f5',
            R: '0x30222c5c4a16e9053492ab90b1555585f2f4da3712de9f3e1f9ca8ce952f4aeb',
            S: '0x621d65429322d3ed961ca04a00cf050ee85b35fa69aaa300a41bf483febdc91' 
        } 
    ],
    status: true,
    to: '0xef5cd886c7f8d85fbe8023291761341acbb4da01',
    transactionHash: '0xbad4dd6d80beda6c04d90f1db7e4179557ab48423d4f14295b33e38a9418e59f',
    transactionIndex: 2,
    type: 'TxTypeValueTransfer',
    typeInt: 8,
    value: '0xde0b6b3a7640000' 
}

The result of the transaction can be found through the status of the receipt. For a detailed description of the return values, see getTransactionReceipt. If a transaction is failed, you can check the detailed error in txError of the receipt. For more information about txError, see txError: Detailed Information of Transaction Failures.

Executing Other Transaction Types

Klaytn provides various transaction types for extensibility and performance. For more information, see Transactions. This section describes various examples that can be used with caver-js.

Fee Delegation

Klaytn provides Fee Delegation feature. Here's an example code.

When you are a sender, use the code below to make an RLP-encoded transaction object:

> caver.klay.accounts.signTransaction({
    type: 'FEE_DELEGATED_VALUE_TRANSFER',
    from: '0x3bd32d55e64d6cbe54bec4f5200e678ee8d1a990',
    to: '0xeF5cd886C7f8d85fbe8023291761341aCBb4DA01',
    gas: '300000',
    value: caver.utils.toPeb('1', 'KLAY'),
  }, '0x{private key}').then((ret)=>{rawTransaction = ret.rawTransaction})

> rawTransaction
'0x09f88d038505d21dba00830493e094ef5cd886c7f8d85fbe8023291761341acbb4da01880de0b6b3a7640000943bd32d55e64d6cbe54bec4f5200e678ee8d1a990f847f8458207f5a0a48374bbf227fbbdcb28f3360d0cc1f5e36922be409a3edd8b0c6fa5aa5c57dda07e15ebe1c9dd78d1c0f36a5f7970e578c2e57d9360cd25928674d1c05d7e161d80c4c3018080'

With the signed RLP-encoded transaction object (rawTransaction), the fee payer can send the transaction after attaching the one's signature. The fee payer sets the rawTransaction to senderRawTransaction and signs with the address of the fee payer, as in the example below.

// If you have not added a fee payer account to caver-js's wallet, add it to your wallet by running 'caver.klay.accounts.wallet.add'.
// If an account is already added to the wallet, 'Error: Account is existed with {hex in address}' is returned. In this case, please use the account's address instead of `feePayer.address`.
> const feePayer = caver.klay.accounts.wallet.add('0x{private key}')

> caver.klay.sendTransaction({
    senderRawTransaction: rawTransaction,
    feePayer: feePayer.address,
  }).then(console.log)
{ 
    blockHash: '0xf0c4ef717a674ffaea8bf68597c936ce8a3773dab1e1f6f42508963f124bc301',
    blockNumber: 3840725,
    ...
    transactionHash: '0x8d1fea7710bc351540257a4ae7f2274d66ddd7f62bcdb6f1f77893cecb659405',
    transactionIndex: 2,
    type: 'TxTypeFeeDelegatedValueTransfer',
    typeInt: 9,
    value: '0xde0b6b3a7640000' 
}

NOTE: The fee payer's account must be in the caver-js wallet.

Account Update

If you want to change the key of the account, send a transaction as shown below. Please check Account Update for the transaction field according to the key type.

// If you have not added an account to caver-js's wallet, add it to your wallet by running 'caver.klay.accounts.wallet.add'.
// If the same account is already in the wallet, 'Error: Account exists with {hex in address}' is returned. In this case, you can use the address string in the `from` field to reference the account in the wallet.
> const account = caver.klay.accounts.wallet.add('0x{private key}')

> caver.klay.sendTransaction({
    type: 'ACCOUNT_UPDATE',
    from: account.address,
    publicKey:  '0x9016de15ebb219b1e8bc732070df93a28903e5799d0cd24a807a5afabf4601f7e5ab312b5a682dd8c0e72e71e67552174d5082cde25db3626a5b025f97f8a005',
    gas: '300000',
}).then(console.log);

Smart Contract

The caver.klay.Contract package makes it easy to interact with smart contracts on Klaytn. It automatically converts all methods of a smart contract into javascript calls when its low-level ABI (Application Binary Interface) is given. This allows you to interact with smart contracts as if they were JavaScript objects.

First, we start by compiling a smart contract to get its bytecode and ABI.

> solc --abi --bin --allow-paths . ./test.sol
======= ./test.sol:Count =======
Binary: 
60806040526000805534801561001457600080fd5b50610123806100246000396000f3fe6080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60df565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260e5565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060dd6004803603602081101560c857600080fd5b810190808035906020019092919050505060ed565b005b60005481565b600043905090565b806000819055505056fea165627a7a72305820e381897039d8e48bf74b4a096bb1c4ed02f331bd1a7a4add6217b72fa888f2f10029
Contract JSON ABI 
[{"constant":true,"inputs":[],"name":"count","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getBlockNumber","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_count","type":"uint256"}],"name":"setCount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]

NOTE: To compile a smart contract, you must have a solidity compiler installed.

For smart contract deployment, you can use caver.klay.Contract to deploy it, or you can deploy it using SMART_CONTRACT_DEPLOY transaction. Here is an example of using caver.klay.Contract.

If the contract instance is created, you can deploy it by passing the bytecode to the data field as shown below:

// If you have not added an account to caver-js's wallet, add it to your wallet by running 'caver.klay.accounts.wallet.add'.
// If the same account is already in the wallet, 'Error: Account exists with {hex in address}' is returned. In this case, you can use the address string in the `from` field to reference the account in the wallet.
> const account = caver.klay.accounts.wallet.add('0x{private key}')

> contractInstance.deploy({
    data:  '60806040526000805534801561001457600080fd5b50610123806100246000396000f3fe6080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60df565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260e5565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060dd6004803603602081101560c857600080fd5b810190808035906020019092919050505060ed565b005b60005481565b600043905090565b806000819055505056fea165627a7a72305820e381897039d8e48bf74b4a096bb1c4ed02f331bd1a7a4add6217b72fa888f2f10029',
}).send({
    from: account.address,
    gas: '0x4bfd200',
    value: '0x0',
}).then(console.log)
{ 
    blockHash: '0x71426773ed65f307bdfac5070ac54f11f406086bbe8dfa170215ed4190f176ed',
    blockNumber: 226,
    codeFormat: '0x0',
    contractAddress: '0xC9f0b868e5103b6823171a2Df85E7B696660E466',
    from: '0x71959675eeb7c7ec1e0c74f206a9c488d7f178d4',
    gas: '0x4bfd200',
    gasPrice: '0x5d21dba00',
    gasUsed: 149017,
    humanReadable: false,
    input: '0x60806040526000805534801561001457600080fd5b50610123806100246000396000f3fe6080604052600436106053576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306661abd14605857806342cbb15c146080578063d14e62b81460a8575b600080fd5b348015606357600080fd5b50606a60df565b6040518082815260200191505060405180910390f35b348015608b57600080fd5b50609260e5565b6040518082815260200191505060405180910390f35b34801560b357600080fd5b5060dd6004803603602081101560c857600080fd5b810190808035906020019092919050505060ed565b005b60005481565b600043905090565b806000819055505056fea165627a7a72305820e381897039d8e48bf74b4a096bb1c4ed02f331bd1a7a4add6217b72fa888f2f10029',
    ...
    type: 'TxTypeSmartContractDeploy',
    typeInt: 40,
    value: '0x0',
    events: {} 
}

The deployed contract address can be found in contractAddress of the transaction receipt. Before sending a smart contract execution transaction, set the address to the address of the contract instance as follows:

> contractInstance.options.address = '0xC9f0b868e5103b6823171a2Df85E7B696660E466'
'0xC9f0b868e5103b6823171a2Df85E7B696660E466'

One way to invoke a specific method of a smart contract is to use it with caver.klay.Contract or use SMART_CONTRACT_EXECUTION.

To transact with a smart contract:

// If you have not added an account to caver-js's wallet, add it to your wallet by running 'caver.klay.accounts.wallet.add'.
// If the same account is already in the wallet, 'Error: Account exists with {hex in address}' is returned. In this case, you can use the address string in the `from` field to reference the account in the wallet.
> const account = caver.klay.accounts.wallet.add('0x{private key}')

> contractInstance.methods.setCount(1).send({from:account.address, gas:'0x4bfd200'}).then(console.log)
{ 
    blockHash: '0x159f8515102951bca9c403b2b1b37850ca01a08dffb9a763837f55a6d518bbb6',
    blockNumber: 644,
    contractAddress: null,
    from: '0x71959675eeb7c7ec1e0c74f206a9c488d7f178d4',
    gas: '0x4bfd200',
    gasPrice: '0x5d21dba00',
    gasUsed: 44875,
    input: '0xd14e62b80000000000000000000000000000000000000000000000000000000000000001',
    ...
    type: 'TxTypeSmartContractExecution',
    typeInt: 48,
    value: '0x0',
    events: {} 
}

To call a smart contract:

> contractInstance.methods.getBlockNumber().call().then(console.log)
2194

See caver.klay.Contract for details.

Using various AccountKey Types

caver-js introduces new classes to support the various types of AccountKey supported by the platform.

The examples below describe the example in a Node.js file. To practice the examples, first create a test file in the working directory as shown below.

$ touch test.js

You can see the test.js file created in the working directory.

Write the following code in test.js.

// test.js file
const Caver = require('caver-js')
const caver = new Caver('https://public-en-baobab.klaytn.net/')

async function testFunction() {
	const version = await caver.klay.getNodeInfo()
	console.log(version)
}

testFunction()

Save the file and run it in your console.

$ node ./test.js

If you see the output of console.log, proceed with the steps below.

NOTE Those classes are supported since caver-js v1.2.0.

Account

Account is a class containing the address and key of an account. The Account has an AccountKey, which can be of type AccountKeyPublic, AccountKeyMultiSig, or AccountKeyRoleBased.

The caver.klay.accounts package uses AccountKeyPublic, which stores and manages a private key string by default.

The following example creates an account with AccountKeyPublic as accountKey.

// test.js file
async function testFunction() {
	// Create random account with accountKeyPublic by default
	const account = caver.klay.accounts.create()
	printAccount(account)

    // Create account with specific private key string
    const privateKey = caver.klay.accounts.create().privateKey
    const accountFromKey = caver.klay.accounts.privateKeyToAccount(privateKey)
    printAccount(accountFromKey)
}

function printAccount(account) {
	console.log(`address: ${account.address}`)
	console.log(`privateKey: ${account.privateKey}`)
	console.log(`accountKeyType: ${account.accountKeyType}`)
	console.log(`accountKey`)
    console.log(account.accountKey)
	console.log(`account.keys: ${account.keys}`)
	console.log(`account.transactionKey: ${account.transactionKey}`)
	console.log(`account.updateKey: ${account.updateKey}`)
	console.log(`account.feePayerKey: ${account.feePayerKey}\n`)
}

The printAccount above shows how to use the properties of the Account instance. The properties inside Account are as follows.

NOTE transactionKey, updateKey, and feePayerKey return a private key string or an array of private key strings that should be used for the role. So rather than using privateKey property, it is recommended that you use transactionKey, updateKey and feePayerKey as appropriate, without worrying about the accountKey type.

An explanation of the various AccountKey classes is provided in the AccountKey part.

AccountKey

AccountKey is a data structure that stores the keys of an account. An account can have one private key string or multiple private key strings to be used for signing. Account can also manage the private keys by roles.

To support this structure, caver-js introduces new classes called AccountKeyPublic, AccountKeyMultiSig, and AccountKeyRoleBased.

To create an AccountKey, use caver.klay.accounts.createAccountKey. This function determines which AccountKey to generate based on the type of the parameter. It creates AccountKeyPublic if a private key string comes as a parameter, or AccountKeyMultiSig if an array of private key strings comes. And if there is an object with a different key for each role, it creates AccountKeyRoleBased.

NOTE The classes for AccountKey defined in caver-js are data structures for storing private keys for use in caver-js. It can be different from the key in your account on Klaytn network.

AccountKeyPublic

AccountKeyPublic is a class for storing and managing a single private key string.

The following describes how to update an account with AccountKeyPublic. Write the following code into testFunction() and run it.

const privateKey = caver.klay.accounts.create().privateKey
const accountKey = caver.klay.accounts.createAccountKey(privateKey)

console.log(accountKey)
console.log(`type: ${accountKey.type}`)
console.log(`keys: ${accountKey.keys}`)
console.log(`transactionKey: ${accountKey.transactionKey}`)
console.log(`updateKey: ${accountKey.updateKey}`)
console.log(`feePayerKey: ${accountKey.feePayerKey}`)

AccountKeyPublic stores and manages a private key string, so if you run the example above, you will see that keys, transactionKey, updateKey and feePayerKey all represent the same private key string.

See below for an example of creating an Account with AccountKeyPublic as its accountKey.

const privateKey = caver.klay.accounts.create().privateKey
const accountKey = caver.klay.accounts.createAccountKey(privateKey)

const address = caver.klay.accounts.create().address

// Create an Account instance with a private key string
const accountFromStringKey = caver.klay.accounts.createWithAccountKey(address, privateKey)

// Create an Account instance with an AccountKeyPublic instance
const accountFromAccountKey = caver.klay.accounts.createWithAccountKey(address, accountKey)

AccountKeyMultiSig

AccountKeyMultiSig is a class for storing and managing multiple private key strings.

The following describes how to update an account with AccountKeyMultiSig. Write the following code into testFunction() and run it.

const privateKeyArray = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey]
const accountKey = caver.klay.accounts.createAccountKey(privateKeyArray)

console.log(accountKey)
console.log(`type: ${accountKey.type}`)
console.log(`keys: ${accountKey.keys}`)
console.log(`transactionKey: ${accountKey.transactionKey}`)
console.log(`updateKey: ${accountKey.updateKey}`)
console.log(`feePayerKey: ${accountKey.feePayerKey}`)

AccountKeyMultiSig stores and manages multiple private key strings, so if you run the example above, you will see that keys, transactionKey, updateKey and feePayerKey all represent the same multiple private key strings.

If you do not specify a private key (or an array of private key strings) to use when signing a transaction, caver-js will find an account from the in-memory wallet that matches the from or fee payer and sign with it. In this case, if your account has multiple private keys, caver-js will sign the transaction with all of those keys.

See below for an example of creating an Account with AccountKeyMultiSig as its accountKey.

const privateKeyArray = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey]
const accountKey = caver.klay.accounts.createAccountKey(privateKeyArray)

const address = caver.klay.accounts.create().address

// Create Account instance with an array of private key strings
const accountFromStringKey = caver.klay.accounts.createWithAccountKey(address, privateKeyArray)

// Create Account instance with AccountKeyMultiSig instance
const accountFromAccountKey = caver.klay.accounts.createWithAccountKey(address, accountKey)

AccountKeyRoleBased

AccountKeyRoleBased is a class for storing and managing keys for each role. Each role can have one private key string or multiple private key strings.

The following describes how to update an account with AccountKeyRoleBased. Write the following code into testFunction() and run it.

const keyobject = {
    transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey],
    updateKey: caver.klay.accounts.create().privateKey,
    feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey]
}
const accountKey = caver.klay.accounts.createAccountKey(keyobject)

console.log(accountKey)
console.log(`type: ${accountKey.type}`)
console.log(`keys:`)
console.log(accountKey.keys)
console.log(`transactionKey: ${accountKey.transactionKey}`)
console.log(`updateKey: ${accountKey.updateKey}`)
console.log(`feePayerKey: ${accountKey.feePayerKey}`)

AccountKeyRoleBased stores and manages keys by role, so if you run the example above, you will see three roles (transactionKey, updateKey, feePayerKey) defined in keys property. Therefore, unlike other AccountKey (AccountKeyPublic or AccountKeyMultiSig), transactionKey, updateKey and feePayerKey each represents a different key.

See below for an example of creating an Account with AccountKeyRoleBased as its accountKey.

const keyobject = {
    transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey],
    updateKey: caver.klay.accounts.create().privateKey,
    feePayerKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey]
}
const accountKey = caver.klay.accounts.createAccountKey(keyobject)

const address = caver.klay.accounts.create().address

// Create Account instance with an object that defines key by role
const accountFromStringKey = caver.klay.accounts.createWithAccountKey(address, keyobject)

// Create Account instance with AccountKeyRoleBased instance
const accountFromAccountKey = caver.klay.accounts.createWithAccountKey(address, accountKey)

Through the above examples you will see how to use Account and various AccountKey types in caver-js.

Note that these examples do not affect the Klaytn network. If you want to use your account with a specific account key type, such as AccountKeyPublic, AccountKeyMultiSig, or AccountKeyRoleBased, you must send an account update transaction to the Klaytn network.

The following AccountForUpdate explains how to update an account by sending a transaction to the Klaytn network.

AccountForUpdate

AccountForUpdate is a class designed to make it easier to use transactions for account updates.

The AccountForUpdate contains only the public key to be used for account update and the address of the account to update.

The examples below start with updating your account with accountKey. There must be enough KLAY in the account to be used for testing. Test KLAY for the Baobab network is available through Baobab Faucet.

Create an AccountForUpdate

Let's start by creating an AccountForUpdate.

You can create it by calling createAccountForUpdate() with the target account address and the new key you want to use.

const account = caver.klay.accounts.create()

// AccountForUpdate with AccountKeyPublic
const privateKeyString = caver.klay.accounts.create().privateKey
const accountForUpdateForAccountKeyPublic = caver.klay.accounts.createAccountForUpdate(account.address, privateKeyString)

// AccountForUpdate with AccountKeyMultiSig
const privateKeyArray = [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey]
const multiSigOptions = { threshold: 2, weight: [1, 1] }
const accountForUpdateForAccountKeyMultiSig = caver.klay.accounts.createAccountForUpdate(account.address, privateKeyArray, multiSigOptions)

// AccountForUpdate with AccountKeyRoleBased
const keyObject = {
    transactionKey: [caver.klay.accounts.create().privateKey, caver.klay.accounts.create().privateKey],
    updateKey: caver.klay.accounts.create().privateKey,
    feePayerKey: caver.klay.accounts.create().privateKey,
}
const roleBasedOptions = { transactionKey: { threshold: 2, weight: [1, 1] } }
const accountForUpdateForAccountKeyRoleBased = caver.klay.accounts.createAccountForUpdate(account.address, keyObject, roleBasedOptions)

// AccountForUpdate with LegacyKey
const accountForUpdateForLegacyKey = caver.klay.accounts.createAccountForUpdateWithLegacyKey(account.address)

// AccountForUpdate with FailKey
const accountForUpdateForFailKey = caver.klay.accounts.createAccountForUpdateWithFailKey(account.address)

NOTE If you want to update with multiple private key strings, you must define thresholds and weights in the options object.

Account update with AccountForUpdate

You can easily create an account update transaction using AccountForUpdate created above.

There are three types of transactions used to update an account: ACCOUNT_UPDATE, FEE_DELEGATED_ACCOUNT_UPDATE and FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO.

In the example below, account is an account that has enough KLAY balance, and accountForUpdate is an AccountForUpdate instance that contains the new key and the target account address. accountForUpdate is created using caver.klay.accounts.createAccountForUpdate`.

The example below demonstrates how to create a transaction using AccountForUpdate and send it to the Klaytn network.

const updateTx = {
    type: 'ACCOUNT_UPDATE',
    from: account.address,
    key: accountForUpdate,
    gas: 300000,
}

// Sign transaction with updateKey of account
const signed = await caver.klay.accounts.signTransaction(updateTx, account.updateKey)

// Send account update transaction
const receipt = await caver.klay.sendSignedTransaction(signed)
console.log(receipt)

// Get accountKey from Klaytn network
const updatedKey = await caver.klay.getAccountKey(account.address)
console.log(updatedKey)

If you want to use FEE_DELEGATED_ACCOUNT_UPDATE transaction, see the example below.

const updateTx = {
    type: 'FEE_DELEGATED_ACCOUNT_UPDATE',
    from: account.address,
    key: accountForUpdate,
    gas: 300000,
}

// Sender signs transaction with updateKey of account
const senderSigned = await caver.klay.accounts.signTransaction(updateTx, account.updateKey)

// Fee payer signs transaction with feePayerKey of fee payer
const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(senderSigned.rawTransaction, feePayer.address, feePayer.feePayerKey)

// Send fee delegated account update transaction
const receipt = await caver.klay.sendSignedTransaction(feePayerSigned)
console.log(receipt)

// Get accountKey from Klaytn network
const updatedKey = await caver.klay.getAccountKey(account.address)
console.log(updatedKey)

NOTE caver.klay.accounts.feePayerSignTransaction is supported since caver-js v1.2.0.

If you want to use FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO transaction, define updateTx in the above example as:

const updateTx = {
    type: 'FEE_DELEGATED_ACCOUNT_UPDATE_WITH_RATIO',
    from: account.address,
    key: accountForUpdate,
    gas: 300000,
    feeRatio: 30,
}

If your account has been updated successfully, the old key can no longer be used. Update the accountKey of the account stored in caver-js as follows.

When updating the accountKey property of an account directly, the assigning value must be an instance of AccountKeyPublic, AccountKeyMultiSig, or AccountKeyRoleBased.

const accountKey = caver.klay.accounts.createAccountKey(newKey)
account.accountKey = accountKey

If your account is in the caver-js in-memory wallet, please update it as below.

// Add account to in-memory wallet
caver.klay.accounts.wallet.add(account)

caver.klay.accounts.wallet.updateAccountKey(account.address, newKey)

You are now ready to use the updated account in caver-js.

Sending a Transaction with multiple signer

If the account's accountKey is AccountKeyMultiSig or AccountKeyRoleBased, the person who manages each key can be different.

This section describes how to collect signatures and send the transaction if there are multiple signers.

Sequential sign

The result object of caver.klay.accounts.signTransaction has a rawTransaction field.

The rawTransaction has an RLP encoded transaction that contains both signatures and feePayerSignatures. feePayerSignature is included only when the transaction is a fee delegated transaction.

The following example shows how to sign a transaction sequentially with multiple private keys. Assume the account's transactionKey has two private key strings.

const tx = {
    type: 'VALUE_TRANSFER',
    from: account.address,
    to: caver.klay.accounts.create().address,
    value: 1,
    gas: 900000,
}

// Sign with transactionKey[0]
const user1Signed = await caver.klay.accounts.signTransaction(tx, account.transactionKey[0])

// Append sender's signatures with transactionKey[1]
const user2Signed = await caver.klay.accounts.signTransaction(user1Signed.rawTransaction, account.transactionKey[1])

const receipt = await caver.klay.sendSignedTransaction(user2Signed)
console.log(receipt)

See the example below for signing with a fee payer's key whose type is an AccountKeyRoleBased. The fee payer is assumed to have three private key strings in feePayerKey.

const tx = {
    type: 'FEE_DELEGATED_VALUE_TRANSFER',
    from: account.address,
    to: caver.klay.accounts.create().address,
    value: 1,
    gas: 900000,
}

// Sign with transactionKey[0] and transactionKey[1]
const userSigned = await caver.klay.accounts.signTransaction(tx, [account.transactionKey[0], account.transactionKey[1]])

// Fee payer signs transaction with feePayerKey[0]
const feePayer1Signed = await caver.klay.accounts.feePayerSignTransaction(userSigned.rawTransaction, feePayer.address, feePayer.feePayerKey[0])

// Append feePayerSignatures with feePayerKey[1] and feePayerKey[2]
const feePayer2Signed = await caver.klay.accounts.feePayerSignTransaction(feePayer1Signed.rawTransaction, feePayer.address, [feePayer.feePayerKey[1], feePayer.feePayerKey[2]])

const receipt = await caver.klay.sendSignedTransaction(feePayer2Signed)
console.log(receipt)

NOTE caver.klay.accounts.feePayerSignTransaction is supported since caver-js v1.2.0.

If the account you use exists in the caver-js in-memory wallet, you do not need to pass the key(s) to signTransaction or feePayerSignTransaction. See the example below.

const tx = {
    type: 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO',
    from: account.address,
    to: caver.klay.accounts.create().address,
    value: 1,
    gas: 900000,
    feeRatio: 10,
}

// Sign with transactionKey[0] and transactionKey[1]
const userSigned = await caver.klay.accounts.signTransaction(tx)

// Fee payer signs transaction with feePayerKey[0], feePayerKey[1] and feePayerKey[2]
const feePayerSigned = await caver.klay.accounts.feePayerSignTransaction(userSigned.rawTransaction, feePayer.address)

const receipt = await caver.klay.sendSignedTransaction(feePayerSigned)
console.log(receipt)

Combine signatures from RawTransaction

If you receive the result object of the caver.klay.accounts.signTransaction or caver.klay.accounts.feePayerSignTransaction from several people, you can create a single RLP encoded transaction that contains all the signature information.

The example below shows how to combine and send the RLP encoded transactions.

const tx = {
    type: 'FEE_DELEGATED_VALUE_TRANSFER',
    from: account.address,
    to: caver.klay.accounts.create().address,
    value: 1,
    gas: 900000,
}

// Sign with transactionKey[0]
const user1Signed = await caver.klay.accounts.signTransaction(tx, account.transactionKey[0])

// Sign with transactionKey[1]
const user2Signed = await caver.klay.accounts.signTransaction(tx, account.transactionKey[1])

// Fee payer signs transaction with feePayerKey[0]
const feePayer1Signed = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address, feePayer.feePayerKey[0])

// Fee payer signs transaction with feePayerKey[1]
const feePayer2Signed = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address, feePayer.feePayerKey[1])

// Fee payer signs transaction with feePayerKey[2]
const feePayer3Signed = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address, feePayer.feePayerKey[2])

const rawTransactionArray = [user1Signed.rawTransaction, user2Signed.rawTransaction, feePayer1Signed.rawTransaction, feePayer2Signed.rawTransaction, feePayer3Signed.rawTransaction]
const combined = await caver.klay.accounts.combineSignatures(rawTransactionArray)

const receipt = await caver.klay.sendSignedTransaction(combined)
console.log(receipt)

NOTE caver.klay.accounts.combineSignatures is supported since caver-js v1.2.0.

Send transaction object with Signatures and FeePayerSignatures

If you only receive signatures or feePayerSignatures from multiple signers, you can send a transaction as follows:

const tx = {
    type: 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO',
    from: account.address,
    to: caver.klay.accounts.create().address,
    value: 1,
    gas: 900000,
    feeRatio: 10,
}

// Sign with transactionKey[0] and transactionKey[1]
const { signatures } = await caver.klay.accounts.signTransaction(tx)

// Fee payer signs transaction with feePayerKey[0], feePayerKey[1] and feePayerKey[2]
const { feePayerSignatures } = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address)

// Fill in the missing information in the tx object.
tx.signatures = signatures
tx.feePayer = feePayer.address
tx.feePayerSignatures = feePayerSignatures

const receipt = await caver.klay.sendSignedTransaction(tx)
console.log(receipt)

You can also call caver.klay.accounts.getRawTransactionWithSignatures to get an RLP encoded transaction containing the signatures and feePayerSignatures of the transaction object.

const tx = {
    type: 'FEE_DELEGATED_VALUE_TRANSFER_WITH_RATIO',
    from: account.address,
    to: caver.klay.accounts.create().address,
    value: 1,
    gas: 900000,
    feeRatio: 10,
}

// Sign with transactionKey[0] and transactionKey[1]
const { signatures } = await caver.klay.accounts.signTransaction(tx)

// Fee payer signs transaction with feePayerKey[0], feePayerKey[1] and feePayerKey[2]
const { feePayerSignatures } = await caver.klay.accounts.feePayerSignTransaction(tx, feePayer.address)

// Fill in the missing information in the tx object.
tx.signatures = signatures
tx.feePayer = feePayer.address
tx.feePayerSignatures = feePayerSignatures

const { rawTransaction } = await caver.klay.accounts.getRawTransactionWithSignatures(tx)
console.log(rawTransaction)

NOTE caver.klay.accounts.getRawTransactionWithSignatures is supported since caver-js v1.2.0.

Sample Projects

Sample projects for development of dApps (Decentralized Applications) using caver-js can be found below:

Last updated