Hi there, friends. Spring is here, and so is caver-js v1.8.0. It will make you laugh, rejoice and appreciate life. (NOT) But trust me, it does have a couple of new features that will make your developer life a bit easier.

If you didn’t know already, now you do: Klaytn supports Ethereum equivalence! And with caver-js v.1.8.0, you can start using Ethereum transaction types and the signatures that go with them. We also fortified our JSON-RPC arsenal for the klay namespace APIs.

## A Breakdown of What’s New in 1.8.0

In line with Klaytn’s pursuit of Ethereum equivalence, Klaytn now supports Ethereum transaction types. You can start using them with caver, via caver.transaction.

The newly added klay namespace API functions are now available with caver via caver.rpc.klay!

And you can use the ecsign function for the new Ethereum transaction types.

Klaytn SDK caver-java v1.8.0 Released

Hi there, lovely Java programmers. You are awesome, don’t let nobody tell you otherwise. We have some new features for caver-java v1.8.0, which are going to make your developer life a bit easier.

If you didn’t know already, now you do: Klaytn supports Ethereum equivalence! And with caver-java v.1.8.0, you can start using Ethereum transaction types and the signatures that go with them. We also fortified our JSON-RPC arsenal for the klay namespace APIs.

## A Breakdown of What’s New in 1.8.0

In line with Klaytn’s pursuit of Ethereum equivalence, Klaytn now supports Ethereum transaction types. You can start using them with caver, via caver.transaction.

The newly added klay namespace API functions are now available with caver via caver.rpc.klay!

And you can use the ecsign function for the new Ethereum transaction types.

Today we are excited to announce that the caver-java (Java Software Development Kit of Klaytn) v1.6.3 has just been released.

The caver-java v1.6.3 now supports the Governance API. Users can use the Governance API via caver.rpc.governance. caver.transaction provides a method that returns a transaction instance provided by Caver from a transaction hash, and caver.abi and caver.contract also support a function to decode functionCall strings encoded with function signatures and parameters. The functions to recover the public key from signature are also provided, and caver.validator newly added in caver-java v1.6.3 provides functions for validating signed messages and transactions.

Supports Governance RPC Call in caver.rpc.governance

Supports decodeFunctionCall

Supports a function that recovers public keys from signatures

Supports caver.transaction.getTransactionByHash

Provides caver.validator

For detailed information about caver-java v1.6.3, please refer to https://github.com/klaytn/caver-java/releases/tag/v1.6.3.

To download caver-java, please visit https://search.maven.org/artifact/com.klaytn.caver/core/1.6.3/jar.

One of the special features of Klaytn is that it has decoupled keys from addresses, so that you can update your keys in the account. In this post, we will be explaining how to update account keys to AccountKeyRoleBased using caver-js and caver-java. For more details on the different types of AccountKey, please refer to Klaytn Docs.

This tutorial assumes that you have the necessary environment setup. If you haven’t yet set up the environment, please refer to caver-js — Prerequisites or caver-java — Prerequisites.

In each section, we use little snippets of the whole code; you can find the in the links below:

1. Creating a keyring

First, create a Klaytn account to use for this tutorial. This account will be used for sending a transaction to make the update to the key stored on the network, so it must possess sufficient KLAY.

Caver uses a structure called Keyring to store private key(s) used in Klaytn accounts.

First, you can create a Keyring instance that stores Klaytn account information with caver-js like this:

// caver-js
const senderKeyring = caver.wallet.keyring.create(senderAddress, senderPrivateKey)

If you have a keystore file instead of a private key string, you can create a keyring using caver.wallet.keyring.decrypt.

And you can create a Keyring instance using caver-java as well:

// caver-java
SingleKeyring senderKeyring = caver.wallet.keyring.create(senderAddress, senderPrivateKey);

For caver-java, you can also create a keyring with caver.wallet.keyring.decrypt, if you have a keystore file.

2. Adding a keyring to caver in-memory wallet

In this exercise, we will be using an in-memory wallet. If you add a keyring to caver’s in-memory wallet, the key stored in that keyring will automatically be used to sign transactions even if you don’t designate a specific key.

This is how you add a keyring to in-memory wallet using caver-js:

// caver-js
caver.wallet.add(senderKeyring)

It is the same for caver-java:

// caver-java
caver.wallet.add(senderKeyring);

3. Creating a new private key

In order to update Klaytn account’s AccountKey to AccountKeyRoleBased, you need private keys defined around roles for your Klaytn account. Here we will use private keys that are randomly generated using the generateRoleBasedKeys function. But if there is a specific private key that you want to use, you can use that one. For more details on each role, please refer to Klaytn Docs.

Below is how you can create multiple private key strings defined for each role to be used with your Klaytn account with caver-js. The array that is sent as a parameter defines the number of keys to be used for each role. The below example will generate two private keys as RoleTransactionKeys, one as RoleAccountUpdateKey, and three as RoleFeePayerKeys.

// caver-js
const newRoleBasedKeys = caver.wallet.keyring.generateRoleBasedKeys([2, 1, 3])

caver-java can also create multiple private key strings using generateRoleBasedKeys:

List<String[]> newRoleBasedKeys = caver.wallet.keyring.generateRolBasedKeys(new int[]{2, 1, 3});

4. Creating a new keyring

Now that we have created new private keys, let’s create a Keyring instance to store the new private keys defined per their respective roles. You can use the Keyring instance once the AccountKey is successfully changed.

You can create a Keyring instance where the private keys for each role are defined using caver-js as shown below:

// caver-js
const newKeyring = caver.wallet.keyring.create(senderKeyring.address, newRoleBasedKeys)

From now on, the newKeyring that stores the new private keys will be used to sign the transaction using newRoleBasedKeys.

Using caver-java, creating the Keyring instance looks like this:

RoleBasedKeyring newKeyring = caver.wallet.keyring.create(senderKeyring.getAddress(), newRoleBasedKeys);

5. Creating an Account instance

The Account class provided by Caver contains the information required to update accounts. To update your key to AccountKeyRoleBased, you need the Klaytn account address that you want to update and the role based keys, where the keys are defined for each role. AccountKeyRoleBased stores the keys in the form of public keys. When multiple keys are used for a role, you can also define the threshold as well as the weight of each public key.

You can create an Account instance by calling the toAccount function of the keyring that stores the newly created private keys. Here is what it looks like when you are using caver-js:

// caver-js
const account = newKeyring.toAccount([{ threshold: 2, weights: [1, 1] }, {}, { threshold: 3, weights: [2, 1, 1] }])

The newKeyring to be used after the update has two RoleTransaction keys and three RoleFeePayer keys, so the threshold and weights can be defined for the keys for each role. When calling the toAccount function, pass the object that defines the WeightedMultisigOptions for each role as parameter. The first element of the array is the object that defines the threshold and weights for the two keys to be used for the RoleTransactionKey. And since only one key is used as RoleAccountUpdateKey, pass an empty object for the second element. The last array element is an object that defines the threshold and weights for the three keys used as RoleFeePayerKey.

You can also create an Account instance by calling the toAccount function of the keyring that stores the new private keys defined according to their roles with caver-java. The threshold of AccountKeyRoleBased and the key’s weights are defined using the WeightedMultiSigOptions class.

// caver-java
BigInteger[][] optionWeight = {
{BigInteger.ONE, BigInteger.ONE},
{},
{BigInteger.valueOf(2), BigInteger.ONE, BigInteger.ONE},
};WeightedMultiSigOptions[] options = {
new WeightedMultiSigOptions(BigInteger.valueOf(2), Arrays.asList(optionWeight[0])),
new WeightedMultiSigOptions(),
new WeightedMultiSigOptions(BigInteger.valueOf(3), Arrays.asList(optionWeight[2])),
};Account account = newKeyring.toAccount(Arrays.asList(options));

For caver-java also, you can create an Account instance by passing the array with WeightedMultiSigOptions defined for each role to the toAccount function.

The new Account instance will now store the address of the Klaytn account to be updated and the keys for each role in the form of public keys. The threshold and weights for the keys will also be stored.

6. Creating a transaction

Once you have created an Account instance, you can use it to create an account update transaction.

Here is how you create a transaction using caver-js:

// caver-js
const accountUpdate = caver.transaction.accountUpdate.create({
from: senderKeyring.address,
account: account,
gas: 150000,})

And here is how you do it using caver-java:

// caver-java
AccountUpdate accountUpdate = caver.transaction.accountUpdate.create(
TxPropertyBuilder.accountUpdate()
.setFrom(senderKeyring.getAddress())
.setAccount(account)
.setGas(BigInteger.valueOf(150000))
);

7. Signing the transaction

Once you have created an account update transaction, you need to sign it using the keyring that was added to the in-memory wallet. Caver’s in-memory wallet, caver.wallet, has a sign function.

Here is how you sign a transaction using caver-js:

// caver-js
await caver.wallet.sign(senderKeyring.address, accountUpdate)

And for caver-java:

// caver-java
caver.wallet.sign(senderKeyring.getAddress(), accountUpdate);

If caver.wallet.sign has been successfully executed, you should see that the signature has been assigned to the signatures field of accountUpdate.

Now all that is left for us to do is to send the transaction to the network.

8. Sending the transaction

Now that we have created and signed the transaction, let’s try sending it to the network. Once this transaction goes through on the network, the old key for your Klaytn account will no longer be usable. Since your old keyring will also no longer be usable, you have to use the new one that stores the new private key.

You can send the signed transaction to the network using caver.rpc.klay.sendRawTransaction.

Below is a sample that shows how to send a transaction with caver-js, using an EventEmitter:

// caver-js
caver.rpc.klay.sendRawTransaction(accountUpdate)
.on(‘transactionHash’, hash => {
console.log(hash)
})
.on(‘receipt’, receipt => {
console.log(receipt)
})

When you are sending a transaction using caver-js, you can get a receipt of the transaction result using Promise with the code below:

// caver-js
const receipt = await caver.rpc.klay.sendRawTransaction(accountUpdate)

You can also send a transaction using caver-java like this:

// caver-java
Bytes32 sendResult = caver.rpc.klay.sendRawTransaction(accountUpdate).send();
String txHash = sendResult.getResult();

When the above code is executed, you will get a transaction hash. The result of the transaction can be obtained with the code as shown below:

// caver-java
public String objectToString(Object value) throws JsonProcessingException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
return ow.writeValueAsString(value);
}TransactionReceiptProcessor receiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);TransactionReceipt.TransactionReceiptData receiptData = receiptProcessor.waitForTransactionReceipt(txHash);System.out.println(objectToString(receiptData));

On the receipt, if the status has returned true, it means that the transaction has been successfully processed. Transaction type is TxTypeAccountUpdate and the updated AccountKeyRoleBased is returned in the key field in encoded form.

{
blockHash: ‘0xe1a010ffef58727d47ed34d071a523c21a661f0c50a0074f8ec2bb8389bf7775’,
blockNumber: ‘0x33ca958’,
contractAddress: null,
from: ‘0x344de28e3e3089c3d7b9076f30dbbafcb329176f’,
gas: ‘0x249f0’,
gasPrice: ‘0x5d21dba00’,
gasUsed: ‘0x226c8’,
key: ‘0x05f8e8b84e04f84b01f848e301a102e2b8b818b0668e26651ed7fa199eccdc9b77e40775db80db6391244175ad6e5ee301a1033c79532bdd5b2c7265754df31f3665ed13a8556c24525a69cf0eedbbf5ff7ef1a302a102670a11eba2c17d92c01dfe272263db6c6d3eed5b1119401f9a62b7023cde6a6ab87204f86f01f86ce301a102104b2f85f43abc7b295cfd3ce91f6cb2c68e47c76d4bcc765b28c6ef2e0a86e8e301a1021190e282cf2a5066013784e08e29fb7821b3044a5f500059575bc003e926528be301a10289a57b9501c831e6b41537d3adbcaeca07ad5685d2963601b282def87ee619f2’,
logs: [],
logsBloom: ‘0x00000…’,
nonce: ‘0x0’,
senderTxHash: ‘0x826e5fbe3f33fd19c9a07b0f315eda2066ce2150b17656825e926374d32ef39a’,
signatures: [
{ V: ‘0x7f5’, R: ‘0xa952b…’, S: ‘0x936ad…’ }
],
status: ‘0x1’,
transactionHash: ‘0x826e5fbe3f33fd19c9a07b0f315eda2066ce2150b17656825e926374d32ef39a’,
transactionIndex: ‘0x0’,
type: ‘TxTypeAccountUpdate’,
typeInt: 32
}

9. Confirming the update of the AccountKey

If the transaction was successful, the account key stored on the network will have been updated. You can check the result using caver.rpc.klay.getAccountKey.

Confirm your new accountKey with caver-js like this:

// caver-js
const accountKey = await caver.rpc.klay.getAccountKey(senderKeyring.address)console.log(accountKey)

Or with caver-java:

// caver-java
AccountKey accountKey = caver.rpc.klay.getAccountKey(senderKeyring.getAddress()).send();System.out.println(objectToString(accountKey));

The code above will return the account keys stored on the Klaytn account. Since the key has been updated to AccountKeyRoleBased, the keyType, represented by the key type ID, is 5. For more details on Account Key Type IDs, please refer to Klaytn Docs.

{
keyType: 5,
key: [
{
keyType: 4,
key: {
threshold: 1,
keys: [
{
weight: 1,
key: { x: ‘0xe2b8b…’, y: ‘0xc8004…’ }
},
{
weight: 1,
key: { x: ‘0x3c795…’, y: ‘0x18799…’ }
}
]
}
},
{
keyType: 2,
key: { x: ‘0x670a1…’, y: ‘0x1fe76…’ }
},
{
keyType: 4,
key: {
threshold: 1,
keys: [
{
weight: 1,
key: { x: ‘0x104b2…’, y: ‘0xa388d…’ }
},
{
weight: 1,
key: { x: ‘0x1190e…’, y: ‘0x3c23b…’ }
},
{
weight: 1,
key: { x: ‘0x89a57…’, y: ‘0xfedd0…’ }
}
]
}
}
]
}

10. Updating in-memory wallet’s keyring

If your account key has been updated with the successfully processed transaction, you have to start using the updated key for signing transactions.

For now, the key that is used in the keyring, and stored in the in-memory wallet, is the old one before the update. Sending a transaction in this state will cause an error, as shown below:

Error: Returned error: invalid transaction v, r, s values of the sender

That is why you also have to update the keyring’s keys stored in the in-memory wallet after updating your Klaytn account key.

The private key used in keyring, and stored in the in-memory wallet can be updated using caver.wallet.updateKeyring. The newKeyring that contains the updated Klaytn account address and new private key will be passed as parameter.

Below is how you can update in-memory wallet’s keyring using caver-js:

// caver-js
caver.wallet.updateKeyring(newKeyring)

And here is how you can do this using caver-java:

// caver-java
caver.wallet.updateKeyring(newKeyring);

11. Sending a transaction with the updated account

If your Klaytn account key has been updated, let’s try sending a transaction using the updated account.

Since the account key stored on the network has been updated, you have to sign the transaction using this new key. In section [10. Updating in-memory wallet’s keyring], we also updated the key in the keyring in the in-memory wallet, so a transaction will be sent with the sign given with this updated key.

Here we will just send a simple value transfer transaction to confirm the update.

Below is a sample that shows how to create, sign and send a ValueTransfer transaction using caver-js:

// caver-js
const vt = caver.transaction.valueTransfer.create({
from: senderKeyring.address,
to: recipientAddress,
value: 1,
gas: 100000,
})

await caver.wallet.sign(senderKeyring.address, vt)

const vtReceipt = await caver.rpc.klay.sendRawTransaction(vt)

Create a transaction using caver.transaction.valueTransfer, and sign it using the caver.wallet.sign function as already demonstrated. Since the keyring in caver.wallet has been updated in [10. Updating in-memory wallet’s keyring], it is the new private keys that will sign the transaction. In this example, the sender signs a ValueTransfer transaction, so the private keys defined in the Role Transaction key will be used for signing. After that, the transaction will be sent to the network using caver.rpc.klay.sendRawTransaction. In this tutorial, the transaction result is returned using Promise.

Below is a sample of the same process, but with caver-java:

// caver-javaValueTransfer vt = caver.transaction.valueTransfer.create(
TxPropertyBuilder.valueTransfer()
.setFrom(senderKeyring.getAddress())
.setTo(recipientAddress)
.setValue(BigInteger.valueOf(1))
.setGas(BigInteger.valueOf(100000))
);caver.wallet.sign(senderKeyring.getAddress(), vt);Bytes32 vtResult = caver.rpc.klay.sendRawTransaction(vt).send();TransactionReceipt.TransactionReceiptData vtReceiptData = receiptProcessor.waitForTransactionReceipt(vtResult.getResult());

Create a ValueTransfer transaction and sign it using the caver.wallet.sign function. The transaction will be sent to the network with caver.rpc.klay.sendRawTransaction. You can confirm the transaction status using the returned transaction hash.

In this post we showed you how to update an account key to AccountKeyRoleBased on Klaytn. This was the last of the series on updating Klaytn account keys. We will come back with a new topic for our next post.

If you encounter any problems following this tutorial or have any questions, feel free to visit Klaytn Forum for help.

Thanks for reading and stay tuned!

The complete code used in this tutorial:

One of the special features of Klaytn is that it has decoupled keys from addresses, so that it’s possible to update your keys in the account. In this post, we will be explaining how to update account keys to AccountKeyWeightedMultiSig using caver-js and caver-java. For more details on the different types of AccountKey, please refer to Klaytn Docs.

This tutorial assumes that you have the necessary environment setup. If you haven’t yet set up the environment, please refer to caver-js — Prerequisites or caver-java — Prerequisites.

In each section, we will go through little snippets of the whole code; you can check the complete code in the links below:

1. Creating a keyring

First, create an Klaytn account to use for this tutorial. This account will be used for sending a transaction to make the update to the key stored on the network, so it must possess sufficient KLAY.

Caver uses a structure called Keyring to store private key(s) used in Klaytn addresses and accounts.

First, you can create a Keyring instance that stores Klaytn account information with caver-js like this:

// caver-js 
const senderKeyring = caver.wallet.keyring.create(senderAddress, senderPrivateKey)

If you have a keystore file instead of a private key string, you can create a keyring using caver.wallet.keyring.decrypt.

And you can create Keyring instance using caver-java as well:

// caver-java
SingleKeyring senderKeyring = caver.wallet.keyring.create(senderAddress, senderPrivateKey);

For caver-java, you can also create a keyring with caver.wallet.keyring.decrypt, if you have a keystore file.

2. Adding a keyring to caver in-memory wallet

In this exercise, we will be using an in-memory wallet. If you add a keyring to caver’s in-memory wallet, the key stored in that keyring will automatically be used to sign transactions even if you don’t designate a specific key.

This is how you add a keyring to in-memory wallet using caver-js:

// caver-js
caver.wallet.add(senderKeyring)

It is the same for caver-java:

// caver-java
caver.wallet.add(senderKeyring);

3. Creating new private keys

In order to update Klaytn account’s AccountKey to AccountKeyWeightedMultiSig, you need private keys for your Klaytn account. Here we will use randomly generated private keys via the generateMultipleKeys function. But if there are specific private keys that you want to use, that is fine.

Below is how you create multiple private key strings for your Klaytn account using caver-js:

// caver-js
const newKeys = caver.wallet.keyring.generateMultipleKeys(3)

You can also generate multiple private key strings using generateMultipleKeys with caver-java:

// caver-java
String[] newKeys = caver.wallet.keyring.generateMultipleKeys(3);

4. Creating a new keyring

Now that we have created new private keys, we will go on to create a Keyring instance to store them. You can start using the Keyring instance with the new keys once the AccountKey in the Klaytn account has been successfully updated.

You can create a Keyring instance that stores the new private keys using caver-js as shown below:

// caver-js
const newKeyring = caver.wallet.keyring.create(senderKeyring.address, newKeys)

The newKeyring that stores the new private keys will sign the transaction using newKeys.

Using caver-java, creating a Keyring instance for storing the new private keys looks like this:

// caver-java
MultipleKeyring newKeyring = caver.wallet.keyring.create(senderKeyring.getAddress(), newKeys);

5. Creating an Account instance

The Account class provided by caver contains the information required to update accounts. When making the update to AccountKeyWeightedMultiSig, you need the Klaytn account address that you want to update, as well as a new weighted multisig key. AccountKeyWeightedMultiSig stores the keys in the form of public keys and defines the threshold, as well as the weight of each public key.

You can create an Account instance by calling the toAccount function of the keyring that stores the newly created private keys. Here is what it looks like when you are using caver-js:

The threshold and the key’s weights of AccountKeyWeightedMultiSig are defined as below:

// caver-js
const account = newKeyring.toAccount({ threshold: 3, weights: [2, 1, 1] })

You can also create an Account instance by calling the keyring’s toAccount function with caver-java. The threshold and the key’s weights for AccountKeyWeightedMultiSig are defined using the WeightedMultiSigOptions class.

BigInteger[] weights = {BigInteger.valueOf(2), BigInteger.ONE, BigInteger.ONE};
WeightedMultiSigOptions options = new WeightedMultiSigOptions(BigInteger.valueOf(3), Arrays.asList(weights));
Account account = newKeyring.toAccount(options);

The Account instance will now store the Klaytn account address to be updated, as well as the keys (in the form of public keys) where the threshold and weights of each key are defined.

6. Creating a transaction

Once you have created an Account instance, you can use it to create an Account Update transaction.

Here is how you create a transaction using caver-js:

// caver-js
const accountUpdate = caver.transaction.accountUpdate.create({
from: senderKeyring.address,
account: account,
gas: 100000,})

And here is how you do it using caver-java:

// caver-java
AccountUpdate accountUpdate = caver.transaction.accountUpdate.create(
TxPropertyBuilder.accountUpdate()
.setFrom(senderKeyring.getAddress())
.setAccount(account)
.setGas(BigInteger.valueOf(100000))
);

7. Signing the transaction

Once you have created an account update transaction, you need to sign it using the keyring that was added to the in-memory wallet. Caver’s in-memory wallet, caver.wallet, has a sign function, just for this purpose.

Here is how you sign a transaction using caver-js:

// caver-js
await caver.wallet.sign(senderKeyring.address, accountUpdate)

And for caver-java:

// caver-java
caver.wallet.sign(senderKeyring.getAddress(), accountUpdate);

If caver.wallet.sign has been successfully executed, you should see that the signature has been assigned to the signatures field of accountUpdate.

Now we are ready to send the transaction to the network.

8. Sending the transaction

Now that we have created and signed the transaction, let’s try sending it to the network. Once this transaction goes through on the network, the old key for your Klaytn account will no longer be usable. Since your old keyring will also no longer be usable, you have to use the new one that stores the new private key.

You can send the signed transaction to the network using caver.rpc.klay.sendRawTransaction.

Below is a sample that shows how to send a transaction with caver-js, using an EventEmitter:

// caver-js
caver.rpc.klay.sendRawTransaction(accountUpdate)
.on('transactionHash', hash => {
console.log(hash)
})
.on('receipt', receipt => {
console.log(receipt)
})

When you are sending a transaction using caver-js, you can get a receipt of the transaction using Promise with the code below:

// caver-js
const receipt = await caver.rpc.klay.sendRawTransaction(accountUpdate)

You can also send a transaction using caver-java like this:

// caver-java
Bytes32 sendResult = caver.rpc.klay.sendRawTransaction(accountUpdate).send();
String txHash = sendResult.getResult();

When the above code is executed, you will get a transaction hash. The result of the transaction can be obtained with the code as shown below:

// caver-java
public String objectToString(Object value) throws JsonProcessingException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
return ow.writeValueAsString(value);
}
TransactionReceiptProcessor receiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);TransactionReceipt.TransactionReceiptData receiptData = receiptProcessor.waitForTransactionReceipt(txHash);System.out.println(objectToString(receiptData));

If the status in the receipt reads true, it means that the transaction has been successfully processed. Transaction type is TxTypeAccountUpdate and the updated AccountKeyWeightedMultiSigcountKeyPublic is returned in the key field in encoded form.

{
blockHash: ‘0xdf8b53b76fc1bc2dde099199026c48b7cae56724b752b1476ae2ab867598a1d5’,
blockNumber: ‘0x33c98c3’,
contractAddress: null,
from: ‘0xf3ecd45eeb2bb22d33d2d83dc2350d18691b5f80’,
gas: ‘0x186a0’,
gasPrice: ‘0x5d21dba00’,
gasUsed: ‘0x13c68’,
key: ‘0x04f86f03f86ce302a103d2df49e8d3b4da20b39a81f422c82c50e29a32b0a001b90d6de72a9cc76535f5e301a103b00854f13c6cb6cabab766c2c4b74bc568568a5600599be6ace18a789fc3b5dce301a1021968807b49413b84eea56d24bfe212166ccafdba2c1ae4d7c1e04a179f28dc5f’,
logs: [],
logsBloom: ‘0x00000…’,
nonce: ‘0x0’,
senderTxHash: ‘0x736f2a35cd4618f00ed4e6a76637b526b2b72e8fdc1a7dab35c3341a2c2027af’,
signatures: [
{ V: ‘0x7f5’, R: ‘0x89e33…’, S: ‘0x1c9a0…’ }
],
status: ‘0x1’,
transactionHash: ‘0x736f2a35cd4618f00ed4e6a76637b526b2b72e8fdc1a7dab35c3341a2c2027af’,
transactionIndex: ‘0x0’,
type: ‘TxTypeAccountUpdate’,
typeInt: 32
}

9. Confirming AccountKey

If the transaction was successful, the account key stored on the network should be updated. You can check the result using caver.rpc.klay.getAccountKey.

Confirm your new accountKey with caver-js like this:

// caver-js
const accountKey = await caver.rpc.klay.getAccountKey(senderKeyring.address)
console.log(accountKey)

Or with caver-java:

// caver-java
AccountKey accountKey = caver.rpc.klay.getAccountKey(senderKeyring.getAddress()).send();
System.out.println(objectToString(accountKey));

The code above will return the account keys stored on the Klaytn account. Since the key has been updated to AccountKeyWeightedMultiSig, the keyType, represented by the key type ID, is 4. For more details on Account Key Type IDs, please refer to Klaytn Docs.

{
keyType: 4,
key: {
threshold: 3,
keys: [
{
weight: 2,
key: { x: ‘0xd2df4…’, y: ‘0x813fe…’ }
},
{
weight: 1,
key: { x: ‘0xb0085…’, y: ‘0x478f5…’ }
},
{
weight: 1,
key: { x: ‘0x19688…’, y: ‘0x9d8b3…’ }
}
]
}
}

10. Updating in-memory wallet’s keyring

If your account key has been updated with the successfully processed transaction, you have to start using the updated key for signing transactions.

For now, the key that is used in the keyring, and stored in the in-memory wallet, is the old one before the update. Sending a transaction in this state will throw an error, as shown below:

Error: Returned error: invalid transaction v, r, s values of the sender

That is why you also have to update the keyring’s keys stored in the in-memory wallet after updating your Klaytn account key.

The private key used in keyring and stored in the in-memory wallet can be updated using caver.wallet.updateKeyring. The newKeyring that contains the updated Klaytn account address and new private key will be passed as parameter.

Below is how you can update in-memory wallet’s keyring using caver-js:

// caver-js
caver.wallet.updateKeyring(newKeyring)

And here is how you can do this using caver-java:

// caver-java
caver.wallet.updateKeyring(newKeyring);

11. Sending a transaction with the updated account

Now that your Klaytn account key has been updated, let’s try sending a transaction using the updated account.

Since the account key stored on the network has been updated, you have to sign the transaction using this new key. In section [10. Updating in-memory wallet’s keyring], we also updated the keys in the keyring in the in-memory wallet, so the transaction will be signed with the updated private keys.

Here we will just send a simple value transfer transaction to confirm the update.

Below is a sample that shows how to create, sign and send a value transfer transaction using caver-js:

// caver-js
const vt = caver.transaction.valueTransfer.create({
from: senderKeyring.address,
to: recipientAddress,
value: 1,
gas: 100000,
})

await caver.wallet.sign(senderKeyring.address, vt)

const vtReceipt = await caver.rpc.klay.sendRawTransaction(vt)

Create a transaction using caver.transaction.valueTransfer, and sign it using the caver.wallet.sign function as already demonstrated. Since the keyring in caver.wallet has been updated in [10. Updating in-memory wallet’s keyring], the transaction will be signed using the new private key. After that, the transaction will be sent to the network using caver.rpc.klay.sendRawTransaction. In this tutorial, the transaction result is returned using Promise.

Below is a sample of the same process, but for caver-java:

// caver-javaValueTransfer vt = caver.transaction.valueTransfer.create(
TxPropertyBuilder.valueTransfer()
.setFrom(sednerKeyring.getAddress())
.setTo(recipientAddress)
.setValue(BigInteger.valueOf(1))
.setGas(BigInteger.valueOf(100000))
);caver.wallet.sign(senderKeyring.getAddress(), vt);Bytes32 vtResult = caver.rpc.klay.sendRawTransaction(vt).send();TransactionReceipt.TransactionReceiptData vtReceiptData = receiptProcessor.waitForTransactionReceipt(vtResult.getResult());

Create a ValueTransfer transaction and sign it using the caver.wallet.sign function. The transaction will be sent to the network with caver.rpc.klay.sendRawTransaction. You can confirm the transaction status using the returned transaction hash.

In this post, we have shown you how to update an account key to AccountKeyWeightedMultiSig. We hope you could follow each step without any problems. In our next post, we will be looking at how to update your key to AccountKeyRoleBased. The process is pretty much similar to this one, but only with a different key type. If you had no problems following this tutorial, the next one should also be a breeze!

If you have any questions or comments, leave them below or visit Klaytn Developers Forum.

Thank you for reading and stay tuned for more!

The complete code used in this tutorial:

One of the cool features of the Klaytn blockchain is that it has decoupled keys from addresses, so that you can update your account key. In this post, we will be explaining how to update your account key to AccountKeyPublic using caver-js and caver-java. For more details on the different types of AccountKey, please refer to Klaytn Docs.

This tutorial assumes that you have the necessary environment setup. If you haven’t yet set up the environment, please refer to caver-js — Prerequisites or caver-java — Prerequisites.

In each section, we will go through little snippets of the whole code; you can check the code in its entirety in the links below.

1. Creating a keyring

First, create an Klaytn account to use for this tutorial. Your account must have sufficient KLAY, because it will be used for sending a transaction to make the necessary update to the key stored on the network.

Caver uses a structure called Keyring to store private key(s) used for Klaytn account addresses and accounts.

Below is how you can create a Keyring instance in caver-js.

// caver-js
const senderKeyring = caver.wallet.keyring.create(senderAddress, senderPrivateKey)

If you have a keystore file instead of a private key string, use caver.wallet.keyring.decrypt to create a Keyring.

And you can create a Keyring instance in caver-java:

// caver-java
SingleKeyring senderKeyring = caver.wallet.keyring.create(senderAddress, senderPrivateKey);

You can also use caver.wallet.keyring.decrypt for java, if you have a keystore file.

2. Adding a keyring to caver in-memory wallet

In this exercise, we will be using an in-memory wallet. If you add a Keyring to caver’s in-memory wallet, the key stored in that Keyring will automatically be used to sign transactions even if you don’t designate a specific key.

This is how you add a Keyring to in-memory wallet using caver-js:

// caver-js
caver.wallet.add(senderKeyring)

It is the same for caver-java:

// caver-java
caver.wallet.add(senderKeyring);

3. Creating a new private key

In order to update Klaytn account’s AccountKey to AccountKeyPublic, you need a private key for your Klaytn account. Here we will use a private key that is randomly generated using the generateSingleKey function. But if there is a specific private key that you want to use, you can use that one.

Below is how you create a new private key string for your Klaytn account using caver-js:

// caver-js
const newKey = caver.wallet.keyring.generateSingleKey()

And for caver-java:

// caver-java
String newKey = caver.wallet.keyring.generateSingleKey();

4. Creating a new keyring

Now that we have created a new private key, we will go on to create a Keyring instance to store this key. You can start using the Keyring instance once the AccountKey in the Klaytn account has been successfully changed.

You can create a Keyring instance that stores the new private key using caver-js as shown below:

// caver-js
const newKeyring = caver.wallet.keyring.create(senderKeyring.address, newKey)

The newKeyring that stores the new private key will sign the transaction using newKey.

Using caver-java, creating a Keyring instance for storing the new private key looks like this:

// caver-java
SingleKeyring newKeyring = caver.wallet.keyring.create(senderKeyring.getAddress(), newKey);

5. Creating an Account instance

The Account class provided by caver contains the information required to update accounts. When making the update to AccountKeyPublic, you need the Klaytn account address that you want to update, as well as the new public key.

You can create an Account instance by calling the toAccount function of the keyring that stores the newly created private key. Here is what it looks like when you are using caver-js:

// caver-js
const account = newKeyring.toAccount()

You can also create an Account instance using caver-java by calling the toAccountfunction:

// caver-java
Account account = newKeyring.toAccount();

The Account instance will now store the Klaytn account address to be updated and the public key derived from the new private key.

6. Creating a transaction

Once you have created an Account instance, you can use it to create an Account Update transaction.

Here is how you create a transaction using caver-js:

// caver-js
const accountUpdate = caver.transaction.accountUpdate.create({
from: senderKeyring.address,
account: account,
gas: 50000,
})

And here is how you do it using caver-java:

// caver-java
AccountUpdate accountUpdate = caver.transaction.accountUpdate.create(
TxPropertyBuilder.accountUpdate()
.setFrom(senderKeyring.getAddress())
.setAccount(account)
.setGas(BigInteger.valueOf(50000))
);

7. Signing the transaction

Once you have created an account update transaction, you need to sign it using the keyring that was added to the in-memory wallet. Caver’s in-memory wallet, caver.wallet, has a sign function.

Here is how you sign a transaction using caver-js:

// caver-js
await caver.wallet.sign(senderKeyring.address, accountUpdate)

And for caver-java:

// caver-java
caver.wallet.sign(senderKeyring.getAddress(), accountUpdate);

If caver.wallet.sign has been successfully executed, you should see that the signature has been assigned to the signatures field of accountUpdate.

Now all that is left for us to do is to send the transaction to the network.

8. Sending the transaction

Now that we have created and signed the transaction, let’s try sending it to the network. Once this transaction goes through on the network, the old key for your Klaytn account will no longer be usable. Since your old keyring will also no longer be usable, you have to use the new one that stores the new private key.

You can send the signed transaction to the network using caver.rpc.klay.sendRawTransaction.

Below is a sample that shows how to send a transaction with caver-js, using an EventEmitter:

// caver-js
caver.rpc.klay.sendRawTransaction(accountUpdate)
.on(‘transactionHash’, hash => {
console.log(hash)
})
.on(‘receipt’, receipt => {
console.log(receipt)
})

When you are sending a transaction using caver-js, you can get a receipt of the transaction result using Promise with the code below:

// caver-js
const receipt = await caver.rpc.klay.sendRawTransaction(accountUpdate)

You can also send a transaction using caver-java like this:

// caver-java
Bytes32 sendResult = caver.rpc.klay.sendRawTransaction(accountUpdate).send();
String txHash = sendResult.getResult();

When the above code is executed, you will get a transaction hash. The result of the transaction can be obtained with the code as shown below:

// caver-java
public String objectToString(Object value) throws JsonProcessingException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
return ow.writeValueAsString(value);
}
TransactionReceiptProcessor receiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);TransactionReceipt.TransactionReceiptData receiptData = receiptProcessor.waitForTransactionReceipt(hash);System.out.println(objectToString(receiptData));

If you see that the status has returned true in the receipt, it means that the transaction has been successfully processed. Transaction type is TxTypeAccountUpdate and the updated AccountKeyPublic is returned in the key field in encoded form.

{
blockHash: ‘0x073ae74db35f15c8f763c89ee4cede3f0a9bfb1256d2b28e2e12f2b4c4ccca18’,
blockNumber: ‘0x33c80b0’,
contractAddress: null,
from: ‘0x405e1c9109684626b5b94177335b2db88e974f86’,
gas: ‘0xc350’,
gasPrice: ‘0x5d21dba00’,
gasUsed: ‘0xa028’,
key: ‘0x02a102259cbde6ac40681c0bdb36a7bd714d7f12214f060a3708a964d80a46a8985d94’,
logs: [],
logsBloom: ‘0x00000…’,
nonce: ‘0x0’,
senderTxHash: ‘0x6d0ccf6afb1b715909244fa8d6affdb9a2fcac74f070cffcb10f6cc1b8b0eb8c’,
signatures: [{ V: ‘0x7f5’, R: ‘0xcc180…’, S: ‘0x161b2…’ }],
status: ‘0x1’,
transactionHash: ‘0x6d0ccf6afb1b715909244fa8d6affdb9a2fcac74f070cffcb10f6cc1b8b0eb8c’,
transactionIndex: ‘0x0’,
type: ‘TxTypeAccountUpdate’,
typeInt: 32
}

9. Confirming AccountKey

If the transaction was successful, the account key stored on the network will have been updated. You can check the result using caver.rpc.klay.getAccountKey.

Confirm your new accountKey in caver-js:

// caver-js
const accountKey = await caver.rpc.klay.getAccountKey(keyring.address)console.log(accountKey)

Or in caver-java:

// caver-java
AccountKey accountKey = caver.rpc.klay.getAccountKey(keyring.getAddress()).send();
System.out.println(objectToString(accountKey));

The above code will return the the account key stored on the Klaytn account. Since the key has been updated to AccountKeyPublic, the keyType, representing a key type ID, is 2. For more details on Account Key Type IDs, please refer to Klaytn Docs.

{
keyType: 2,
key: {
x: ‘0x259cb…’,
y: ‘0x707b7…’
}
}

10. Updating in-memory wallet’s keyring

If your account key has been updated with the successfully processed transaction, you have to start using the updated key for signing transactions.

For now, the key that is used in the keyring, and stored in the in-memory wallet, is the old one before the update. Sending a transaction in this state will cause an error, as shown below:

Error: Returned error: invalid transaction v, r, s values of the sender

That is why you also have to update the keyring’s key stored in the in-memory wallet after updating your Klaytn account key.

The private key used in keyring, and stored in the in-memory wallet can be updated using caver.wallet.updateKeyring. The newKeyring that contains the updated Klaytn account address and new private key will be passed as parameter.

Below is how you can update in-memory wallet’s keyring using caver-js:

// caver-js
caver.wallet.updateKeyring(newKeyring)

And here is how you can do this using caver-java:

// caver-java
caver.wallet.updateKeyring(newKeyring);

11. Sending a transaction with the updated account

Your Klaytn account key has been updated, now let’s try sending a transaction using the updated account.

Since the account key stored on the network has been updated, you will now have to sign the transaction using the new key. In section [10. Updating in-memory wallet’s keyring], we also updated the key in the keyring in the in-memory wallet, so when a transaction is sent, it will be signed with this updated key.

Here we will just send a simple value transfer transaction to confirm the update.

The sample code below shows how to create, sign and send a valueTransfer transaction using caver-js:

// caver-js
const vt = caver.transaction.valueTransfer.create({
from: senderKeyring.address,
to: recipientAddress,
value: 1,
gas: 25000,
})

await caver.wallet.sign(senderKeyring.address, vt)

const vtReceipt = await caver.rpc.klay.sendRawTransaction(vt)

Create a transaction using caver.transaction.valueTransfer, and sign it using the caver.wallet.sign function as already demonstrated. Since the keyring in caver.wallet has been updated in [10. Updating in-memory wallet’s keyring], the new private key will be used to sign the transaction. After that, the transaction will be sent to the network using caver.rpc.klay.sendRawTransaction. In this tutorial, the transaction result is returned using Promise.

Below is the same process in caver-java:

// caver-java
ValueTransfer vt = caver.transaction.valueTransfer.create(
TxPropertyBuilder.valueTransfer()
.setFrom(senderKeyring.getAddress())
.setTo(recipientAddress)
.setValue(BigInteger.valueOf(1))
.setGas(BigInteger.valueOf(25000))
);

caver.wallet.sign(senderKeyring.getAddress(), vt);

Bytes32 vtResult = caver.rpc.klay.sendRawTransaction(vt).send();

TransactionReceipt.TransactionReceiptData vtReceiptData = receiptProcessor.waitForTransactionReceipt(vtResult.getResult());

Create a ValueTransfer transaction and sign it using the caver.wallet.sign function. The transaction will be sent to the network with caver.rpc.klay.sendRawTransaction. You can confirm the transaction status using the returned transaction hash.

In this post, we have demonstrated how to update an account key to AccountKeyPublic. We hope that the explanation was straightforward.

In our next post, we will be looking at how to update your key to AccountKeyWeightedMultiSig. The process is pretty much similar to this one, only with a different key type. If you had no problems following this tutorial, the next one should also be a breeze!

Source code links used in this post:

If you have any questions, please visit our Developer Forum. 😉

Thank you for reading and stay tuned for more posts!

Starting from caver v1.6.1, caver.contract supports the fee delegation model. In this post, we will explain how to use fee delegation when using Contract with Caver.

In each section, we will show you little code snippets; you can check the entire code in the links below.

📢 Note
Your account needs to have a sufficient KLAY balance in order to execute the code shown in this tutorial.

1. Adding test account to in-memory wallet

Before deploying and executing a smart contract using caver.contract, you need to add an account with sufficient KLAY to the in-memory caver.wallet. For this you need a deployer account that sends the transaction, and a feePayer account that pays the transaction fee.

This is how you add an account to caver.wallet using caver-js:

// Create keyrings and add to the in-memory wallet - caver-js
const deployerAddress = '0x{address}'
const deployerPrivateKey = '0x{private key}'
const feePayerAddress = '0x{address}'
const feePayerPrivateKey = '0x{private key}'

const deployerKeyring = caver.wallet.keyring.create(deployerAddress, deployerPrivateKey)
caver.wallet.add(deployerKeyring)const feePayerKeyring = caver.wallet.keyring.create(feePayerAddress, feePayerPrivateKey)
caver.wallet.add(feePayerKeyring)

Create a Keyring instance with the address and the private key to be used in the test account with the caver.wallet.keyring.create function as parameter. The Keyring instance can then be added to the in-memory wallet using the caver.wallet.add function.

If you have a keystore instead of a private key, you can create a Keyring instance using caver.wallet.keyring.decrypt. If your account key has been updated with a decoupled key, you can create the Keyring instance using caver.wallet.keyring.create.

You can also use the caver.wallet.add function for caver-java as below:

// Create a keyring and add to the in-memory wallet - caver-java
String deployerAddress = "0x{address}";
String deployerPrivateKey = "0x{private key}";String feePayerAddress = "0x{address}";
String feePayerPrivateKey = "0x{private key}";

SingleKeyring deployerKeyring = caver.wallet.keyring.create(deployerAddress, deployerPrivateKey);
caver.wallet.add(deployerKeyring);

SingleKeyring feePayerKeyring = caver.wallet.keyring.create(feePayerAddress, feePayerPrivateKey);
caver.wallet.add(feePayerKeyring);

2. Deploying smart contract

In this section, we will be demonstrating a simple smart contract that maps keys with values. The contract looks like this:

pragma solidity ^0.5.6;

contract KVstore {
mapping(string=>string) store;

constructor (string memory key, string memory value) public {
store[key] = value;
}

function get(string memory key) public view returns (string memory) {
return store[key];
}

function set(string memory key, string memory value) public {
store[key] = value;
}
}

Below shows how you can deploy a smart contract using fee delegation with caver-js. The abi and byteCode in the source code below are the result of compiling the smart contract above.

// Deploy a smart contract to the Klaytn - caver-js
const byteCode = '0x608060405234801561001057600080fd5b5060405161072d38038061072d8339810180604052604081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190602001805164010000000081111561009a57600080fd5b828101905060208101848111156100b057600080fd5b81518560018202830111640100000000821117156100cd57600080fd5b5050929190505050806000836040518082805190602001908083835b6020831061010c57805182526020820191506020810190506020830392506100e9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051906020019061015292919061015a565b5050506101ff565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061019b57805160ff19168380011785556101c9565b828001600101855582156101c9579182015b828111156101c85782518255916020019190600101906101ad565b5b5090506101d691906101da565b5090565b6101fc91905b808211156101f85760008160009055506001016101e0565b5090565b90565b61051f8061020e6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063693ec85e1461003b578063e942b5161461016f575b600080fd5b6100f46004803603602081101561005157600080fd5b810190808035906020019064010000000081111561006e57600080fd5b82018360208201111561008057600080fd5b803590602001918460018302840111640100000000831117156100a257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506102c1565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610134578082015181840152602081019050610119565b50505050905090810190601f1680156101615780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102bf6004803603604081101561018557600080fd5b81019080803590602001906401000000008111156101a257600080fd5b8201836020820111156101b457600080fd5b803590602001918460018302840111640100000000831117156101d657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561023957600080fd5b82018360208201111561024b57600080fd5b8035906020019184600183028401116401000000008311171561026d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506103cc565b005b60606000826040518082805190602001908083835b602083106102f957805182526020820191506020810190506020830392506102d6565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103c05780601f10610395576101008083540402835291602001916103c0565b820191906000526020600020905b8154815290600101906020018083116103a357829003601f168201915b50505050509050919050565b806000836040518082805190602001908083835b6020831061040357805182526020820191506020810190506020830392506103e0565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051906020019061044992919061044e565b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061048f57805160ff19168380011785556104bd565b828001600101855582156104bd579182015b828111156104bc5782518255916020019190600101906104a1565b5b5090506104ca91906104ce565b5090565b6104f091905b808211156104ec5760008160009055506001016104d4565b5090565b9056fea165627a7a72305820adabefbb9574a90843d986f100c723c37f37e79f289b16aa527705b5341499aa0029'const abi = [
{
constant: true,
inputs: [{ name: 'key', type: 'string' }],
name: 'get',
outputs: [{ name: '', type: 'string' }],
payable: false,
stateMutability: 'view',
type: 'function',
},
{
constant: false,
inputs: [{ name: 'key', type: 'string' }, { name: 'value', type: 'string' }],
name: 'set',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [{ name: 'key', type: 'string' }, { name: 'value', type: 'string' }],
payable: false,
stateMutability: 'nonpayable',
type: 'constructor',
},]

let contract = caver.contract.create(abi)const keyString = 'keyString'// Send a FeeDelegatedSmartContractDeploy transaction directly to Klaytn.contract = await contract.deploy({
from: deployerKeyring.address,
feeDelegation: true,
feePayer: feePayerKeyring.address,
gas: 1000000,}, byteCode, keyString, 'valueString')console.log(`The address of deployed smart contract: ${contract.options.address}`)

Create a Contract instance by passing the smart contract’s ABI (Application Binary Interface) as a parameter of the caver.contract.create function. And then deploy the smart contract on Klaytn using the contract.deploy method. The first parameter of the method is the options object, where the values required for sending transactions are defined. From the example above, you can see that feeDelegation: true and feePayer are defined inside the options object. If the feeDelegation field is true, fee delegation transaction will be used for deploying smart contracts. The FeeDelegatedSmartContractDeployWithRatio transaction will be used if the feeRatio field is defined in the options object; otherwise FeeDelegatedSmartContractDeploy transaction will be used.

contract.deploy from the above example creates aFeeDelegatedSmartContractDeploy transaction to deploy a smart contract. Since the keyring of the deployer and fee payer keyring managed by caver.wallet are used for signing transactions before being sent to Klaytn, the fee payer account needs to be defined in the feePayer field when using fee delegation. Pass the byte code of the smart contract as the second parameter, followed by the constructor parameters of the contract.

contract.deploy returns the Contract instance that stores the address of the deployed smart contract.

If the transaction cannot be sent right away because the deployer and fee payer are different , you need a separate signature from each of them.

// The deployer signs the transaction to deploy a smart contract. - caver-js
const deployTx = await contract.sign({
from: deployerKeyring.address,
feeDelegation: true,
gas: 1000000,
}, 'constructor', byteCode, keyString, 'valueString')
console.log(`Deployer signed transaction: `)
console.log(deployTx)

const rlpEncoded = deployTx.getRLPEncoding()

You can get the transaction with the deployer’s signature using the contract.sign function. In the example above, the options object, which includes the data required for creating a transaction, is the first parameter of the contract.sign function. You will notice constructor in the place of the second parameter. contract.sign requires the name of the method as the second parameter, but if you are deploying a smart contract, use constructor instead.

You don’t have to define feePayer in options for contract.sign, since it is simply for adding the deployer’s signature to the transaction. The signedFeeDelegatedSmartContractDeploy transaction returned from contract.sign can be retrieved as an RLP-encoded string using the getRLPEncoding function.

// The fee payer signs the transaction to deploy a smart contract. - caver-js
const deployTx = caver.transaction.decode(rlpEncoded)

await caver.wallet.signAsFeePayer(feePayerKeyring.address, deployTx) // Signs the transaction as a fee payer
const receipt = await caver.rpc.klay.sendRawTransaction(deployTx)
console.log(`The address of deployed smart contract: ${receipt.contractAddress}`)
contract = caver.contract.create(abi, receipt.contractAddress)

The fee payer receives the RLP-encoded string and creates a transaction instance using caver.transaction.decode. The fee payer can sign the FeeDelegatedSmartContractDeploy transaction, returned from caver.transaction.decode, using caver.wallet.signAsFeePayer. Once the transaction has been signed by both the deployer and fee payer, it can be sent to the Klaytn network using caver.rpc.klay.sendRawTransaction.

Use contract.signAsFeePayer if the fee payer has to give the signature first. contract.signAsFeePayer returns the transaction with the fee payer’s signature added to feePayerSignatures. After that the deployer’s signature is added using caver.wallet.sign and the transaction is sent to Klaytn using caver.rpc.klay.sendRawTransaction.

With caver-java, you can deploy a smart contract with fee delegation as shown below:

// Deploy a smart contract to the Klaytn - caver-javaString byteCode = "0x608060405234801561001057600080fd5b5060405161072d38038061072d8339810180604052604081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190602001805164010000000081111561009a57600080fd5b828101905060208101848111156100b057600080fd5b81518560018202830111640100000000821117156100cd57600080fd5b5050929190505050806000836040518082805190602001908083835b6020831061010c57805182526020820191506020810190506020830392506100e9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051906020019061015292919061015a565b5050506101ff565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061019b57805160ff19168380011785556101c9565b828001600101855582156101c9579182015b828111156101c85782518255916020019190600101906101ad565b5b5090506101d691906101da565b5090565b6101fc91905b808211156101f85760008160009055506001016101e0565b5090565b90565b61051f8061020e6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063693ec85e1461003b578063e942b5161461016f575b600080fd5b6100f46004803603602081101561005157600080fd5b810190808035906020019064010000000081111561006e57600080fd5b82018360208201111561008057600080fd5b803590602001918460018302840111640100000000831117156100a257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506102c1565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610134578082015181840152602081019050610119565b50505050905090810190601f1680156101615780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102bf6004803603604081101561018557600080fd5b81019080803590602001906401000000008111156101a257600080fd5b8201836020820111156101b457600080fd5b803590602001918460018302840111640100000000831117156101d657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561023957600080fd5b82018360208201111561024b57600080fd5b8035906020019184600183028401116401000000008311171561026d57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506103cc565b005b60606000826040518082805190602001908083835b602083106102f957805182526020820191506020810190506020830392506102d6565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156103c05780601f10610395576101008083540402835291602001916103c0565b820191906000526020600020905b8154815290600101906020018083116103a357829003601f168201915b50505050509050919050565b806000836040518082805190602001908083835b6020831061040357805182526020820191506020810190506020830392506103e0565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020908051906020019061044992919061044e565b505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061048f57805160ff19168380011785556104bd565b828001600101855582156104bd579182015b828111156104bc5782518255916020019190600101906104a1565b5b5090506104ca91906104ce565b5090565b6104f091905b808211156104ec5760008160009055506001016104d4565b5090565b9056fea165627a7a72305820adabefbb9574a90843d986f100c723c37f37e79f289b16aa527705b5341499aa0029";
String abi = "[\n" +
" {\n" +
" \"constant\":true,\n" +
" \"inputs\":[\n" +
" {\n" +
" \"name\":\"key\",\n" +
" \"type\":\"string\"\n" +
" }\n" +
" ],\n" +
" \"name\":\"get\",\n" +
" \"outputs\":[\n" +
" {\n" +
" \"name\":\"\",\n" +
" \"type\":\"string\"\n" +
" }\n" +
" ],\n" +
" \"payable\":false,\n" +
" \"stateMutability\":\"view\",\n" +
" \"type\":\"function\"\n" +
" },\n" +
" {\n" +
" \"constant\":false,\n" +
" \"inputs\":[\n" +
" {\n" +
" \"name\":\"key\",\n" +
" \"type\":\"string\"\n" +
" },\n" +
" {\n" +
" \"name\":\"value\",\n" +
" \"type\":\"string\"\n" +
" }\n" +
" ],\n" +
" \"name\":\"set\",\n" +
" \"outputs\":[],\n" +
" \"payable\":false,\n" +
" \"stateMutability\":\"nonpayable\",\n" +
" \"type\":\"function\"\n" +
" },\n" +
" {\n" +
" \"inputs\":[\n" +
" {\n" +
" \"name\":\"key\",\n" +
" \"type\":\"string\"\n" +
" },\n" +
" {\n" +
" \"name\":\"value\",\n" +
" \"type\":\"string\"\n" +
" }\n" +
" ],\n" +
" \"payable\":false,\n" +
" \"stateMutability\":\"nonpayable\",\n" +
" \"type\":\"constructor\"\n" +
" }\n" +
"]";Contract contract = caver.contract.create(abi);
String keyString = "keyString";

SendOptions sendOptionsForDeployment = new SendOptions();
sendOptionsForDeployment.setFrom(deployerKeyring.getAddress());
sendOptionsForDeployment.setGas(BigInteger.valueOf(1000000));
sendOptionsForDeployment.setFeeDelegation(true);
sendOptionsForDeployment.setFeePayer(feePayerKeyring.getAddress());

// Send a FeeDelegatedSmartContractDeploy transaction to the Klaytn directly.
contract.deploy(sendOptionsForDeployment, byteCode, keyString, "valueString");
System.out.println("The address of deployed smart contract:" + contract.getContractAddress());

If the transaction cannot be sent right away because the deployer and fee payer are different, you need a separate signature from each of them.

// The deployer and fee payer each sign the transaction to deploy a smart contract. - caver-java
SendOptions sendOptionsForSigning = new SendOptions();
sendOptionsForSigning.setFrom(deployerKeyring.getAddress());
sendOptionsForSigning.setGas(BigInteger.valueOf(1000000));
sendOptionsForSigning.setFeeDelegation(true);
AbstractTransaction signedTx = contract.sign(sendOptionsForSigning, "constructor", byteCode, keyString, "valueString");String rlpEncoded = signedTx.getRLPEncoding();// The fee payer signs the transaction to deploy a smart contract. - caver-java
AbstractTransaction signedTx = caver.transaction.decode(rlpEncoded);

caver.wallet.signAsFeePayer(feePayer.getAddress(), (AbstractFeeDelegatedTransaction)signedTx);
Bytes32 sendResult = caver.rpc.klay.sendRawTransaction(signedTx).send();String txHash = sendResult.getResult();
TransactionReceiptProcessor receiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);

TransactionReceipt.TransactionReceiptData receiptData = receiptProcessor.waitForTransactionReceipt(txHash);
System.out.println("The address of deployed smart contract:" + receiptData.getContractAddress());

3. Calling the smart contract function

3.1. call

To confirm that the key and value passed to the constructor have been stored properly, call the get function in the smart contract. The get function simply returns data without changing the contract state. You can call function using contract.call.

Below is how you call the get function using caver-js:

// Read value from smart contract. - caver-js
let valueString = await contract.call('get', keyString)
console.log(`key: ${keyString} / value: ${valueString}`)

You can call the smart contract functions using contract.call. Pass the function name as the first parameter, and the parameters for that function as the second parameter. If you want to define options when calling the function, pass the object as the very first parameter. For more details, please refer to Klaytn Docs.

Below is how you can call the get function using caver-java:

// Read value from smart contract. - caver-java
Utf8String valueString = (Utf8String)contract.call("get", keyString).get(0);
System.out.println("key: " + keyString + " / value: " + valueString.toString());

3.2. send

To make changes to the data stored in the smart contract, call the set function of the deployed smart contract. The set function makes changes to the smart contract’s state, and you need to send a transaction to call it. You can call the function using contract.sendcontract.send will accept the object where the values required for creating a transaction are defined as the first parameter. When you are using fee delegation, you can additionally define feeDelegationfeePayer and feeRatio in the object.

Below is how to call the set function using caver-js:

// Send a FeeDelegatedSmartContractExecutionWithRatio transaction to the Klaytn directly. - caver-js
const setResult = await contract.send({
from: deployerKeyring.address,
feeDelegation: true,
feePayer: feePayerKeyring.address,
feeRatio: 50, // Without feeRatio, `send` will use FeeDelegatedSmartContractExecution
gas: 1000000,
}, 'set', keyString, 'anotherValue')

In the object passed as the first parameter for contract.sendfeeDelegation is defined as true. This means that you will be using fee delegation to execute the set function. Since feeRatio is also defined, FeeDelegatedSmartContractExecutionWithRatio transaction will be used. Pass the name of the function that you want to call as the second parameter, followed by the parameters required by the function. After contract.send creates a transaction, it requires both the deployer and fee payer to give their signatures before sending it to Klaytn. Therefore, feePayer needs to be defined in the first parameter if you are using fee delegation.

If the transaction cannot be sent right away because the deployer and the fee payer are different, they can sign the transactions separately as shown below:

// The deployer and fee payer each sign the transaction to execute a smart contract. - caver-js
const executionTx = await contract.sign({
from: deployerKeyring.address,
feeDelegation: true,
feeRatio: 50, // Without feeRatio, `send` will use FeeDelegatedSmartContractExecution
gas: 1000000,
}, 'set', keyString, 'anotherValue')
console.log(`Deployer signed transaction: `)
console.log(executionTx)

await caver.wallet.signAsFeePayer(feePayerKeyring.address, executionTx) // Signs the transaction as a fee payer
const setResult = await caver.rpc.klay.sendRawTransaction(executionTx)

Deployer signs the transaction using the contract.sign function and adds the signature of the fee payer using the caver.wallet.signAsFeePayer function. Once the transaction is signed, it will be sent to the Klaytn network using caver.rpc.klay.sendRawTransaction.

Once the above transaction has been executed, you can use contract.call to look at the result.

// Read value string from the smart contract. - caver-jsvalueString = await contract.call('get', keyString)
console.log(`After executing set function => key: ${keyString} / value: ${valueString}`)

In caver-java, call the set function like this:

// Send a FeeDelegatedSmartContractExecutionWithRatio transaction to the Klaytn directly. - caver-java
SendOptions sendOptionsForExecution = new SendOptions();
sendOptionsForExecution.setFrom(deployer.getAddress());
sendOptionsForExecution.setGas(BigInteger.valueOf(1000000));
sendOptionsForExecution.setFeeDelegation(true);
sendOptionsForExecution.setFeePayer(feePayer.getAddress());
sendOptionsForExecution.setFeeRatio(BigInteger.valueOf(50)); // Without feeRatio, `send` will use FeeDelegatedSmartContractExecutionTransactionReceipt.TransactionReceiptData receiptData = contract.send(sendOptionsForExecution, "set", "key_inserted", "value_inserted");
System.out.println("Transaction type : " + receiptData.getType());
System.out.println("From : " + receiptData.getFrom());
System.out.println("Fee payer : " + receiptData.getFeePayer());
System.out.println("Fee ratio : " + receiptData.getFeeRatio());

If you need separate signatures from the deployer and fee payer for caver-java:

// The deployer and fee payer each sign the transaction to execute a smart contract. - caver-javaSendOptions sendOptionsForExecution = new SendOptions();
sendOptionsForExecution.setFrom(deployer.getAddress());
sendOptionsForExecution.setGas(BigInteger.valueOf(1000000));
sendOptionsForExecution.setFeeDelegation(true);
sendOptionsForExecution.setFeeRatio(BigInteger.valueOf(50)); // Without feeRatio, `send` will use FeeDelegatedSmartContractExecution

AbstractTransaction executionTx = contract.sign(sendOptionsForExecution, "set", "key_inserted", "value_inserted");
caver.wallet.signAsFeePayer(feePayer.getAddress(), (AbstractFeeDelegatedTransaction)executionTx);

Bytes32 sendResult = caver.rpc.klay.sendRawTransaction(executionTx).send();String txHash_executed = sendResult.getResult();
TransactionReceiptProcessor receiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);

TransactionReceipt.TransactionReceiptData receiptData = receiptProcessor.waitForTransactionReceipt(txHash_executed);

Once the transaction has been sent, use contract.call to check the result.

// Use short-cut function to read value from smart contract. - caver-java
Utf8String value = (Utf8String)contract.call("get", keyString).get(0);
System.out.println("After executing set function => key: " + keyString + "value: " + value.toString());

Entire Source Code from the Article

We hope this post was helpful for you. For any questions or issues, leave them in the comment section or in our Klaytn Developer Forum.

Thank you for reading!