Technology, Tutorials, for Developer

How to use web3klaytn SDK

Introduction

As an Ethereum-equivalent blockchain, Klaytn developers have always been able to use Ethereum-based SDKs such as ethersjs, web3js, web3j, web3py, and so on. However, these are not optimized for Klaytn, which makes it difficult for developers to use them while taking advantage of Klaytn’s unique features such as transaction fee, contract execution fee, multisig key, role-based key, and more.

While the caver-js/java SDK is perfectly usable, we felt that it falls short in a few key areas, and developed web3klaytn for these reasons:

  • Seamless development: Many DApp developers on Ethereum are drawn to Klaytn’s fast transaction speed and responsiveness, and having a Klaytn-specific SDK will make it easier for developers to onboard, and launch services in the Klaytn ecosystem.
  • Hardhat integration: As Hardhat uses ethers.js by default, developers using it to write and test contracts will be able to add web3klaytn’s ethers.js version, ethers-ext, to use Klaytn’s specialized features within Hardhat.
  • Multichain development: Multichain DApps are the future, and having an SDK that can be used across multiple different chains will make development and maintenance easier, providing an environment that allows developers to focus on perfecting their DApps.

With this new web3klaytn SDK, developers will now have access to a set of library extensions that add Klaytn-related functionality to existing Ethereum-compatible web3 SDKs. It provides four extensions for four libraries that are commonly used in web3 development to help you take advantage of Klaytn’s unique features without interfering with or replacing the functionality of existing libraries.

Let’s take a closer look at web3klaytn’s features and how to use them.

Sending Klaytn-type transactions with ethers-ext

Beyond the legacy transaction type, Klaytn also supports many other transaction types, such as value-transfer which you can use with just two additional lines as shown in the Javascript code below.

// npm install --save @klaytn/ethers-ext
const ethers = require("ethers");
const { Wallet, Klaytn } = require("@klaytn/ethers-ext");
const provider = new ethers.providers.JsonRpcProvider('https://public-en-baobab.klaytn.net')
const wallet = new Wallet(senderPriv, provider);
let tx = {
    type: TxType.ValueTransfer,
    to: recieverAddr,
    value: parseKlay("1"),
    from: senderAddr,
}; 
const sentTx = await wallet.sendTransaction(tx);
const receipt = await sentTx.wait();
console.log('receipt', receipt);

In addition to value-transfer, other transaction types such as fee delegation and account update are also supported to provide builders with greater flexibility and performance optimization. With ethers-ext, you can utilize these Klaytn transaction types with only a few additions to your existing ethers.js code by simply adding a “type” field to the transaction object and the appropriate fields for the transaction type. Examples for the other transaction types can be found on the ethers-ext GitHub.

Creating a Fee Delegation Server with web3py-ext

Klaytn’s fee delegation is one of our most popular features, as it allows new users to start experiencing web3 without having to first acquire and hold KLAY in their wallet for gas—massively simplifying the user onboarding process. 

Generally, gas payment works like this: the user signs a transaction once on the client side, the payment server receives it, further signs the transaction with the payment account, and sends it to the network. With web3py-ext, you can create a simple gas payment client and server as shown in the Python code below.

# fee_delegation_client.py
# pip install web3py-ext
import socket
from web3py_ext import extend
from web3 import Web3
from eth_account import Account
from web3py_ext.transaction.transaction import (
    empty_tx,
    fill_transaction,
    TX_TYPE_FEE_DELEGATED_VALUE_TRANSFER
)
from cytoolz import merge

w3 = Web3(Web3.HTTPProvider('https://public-en-baobab.klaytn.net'))

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
    client_socket.connect(('', 5555)) # localhost:5555

    user = Account.from_key(“user private key”)
    fee_delegated_tx = empty_tx(TX_TYPE_FEE_DELEGATED_VALUE_TRANSFER)
    fee_delegated_tx = merge(fee_delegated_tx, {
        'from' : user.address,
        'to' : user.address,   # send to self for testing
        'value' : Web3.to_peb(0.1, 'klay'),
    })
    fee_delegated_tx = fill_transaction(fee_delegated_tx, w3)
    
    # sign the klaytn specific transaction type with web3py
    signed_tx = Account.sign_transaction(fee_delegated_tx, user.key)
    raw_tx = signed_tx.rawTransaction.hex()
    print("\nsent raw tx to fee payer:", raw_tx)
    client_socket.send(raw_tx.encode())
    print("\ntx hash:", client_socket.recv(1024).decode())

The syntax “from web3py_ext import extend” extends the existing web3.py with functionality for Klaytn. Web3py-ext provides empty_tx and fill_transaction functions to fill the fields according to the transaction type. After entering the transaction contents, the user signs the transaction with their private key and sends the signed transaction to the payment server instead of the Klaytn network.

Following that, the fee delegation server receives the transaction signed by the user, adds the payer’s signature, and sends it to the Klaytn network. The gas fee of the transaction will be borne by the payer’s account. 

# fee_delegation_server.py
# pip install web3py-ext
import socket
from hexbytes import HexBytes
from web3py_ext import extend
from web3 import Web3
from eth_account import Account
from web3py_ext.utils.klaytn_utils import to_pretty

w3 = Web3(Web3.HTTPProvider('https://public-en-baobab.klaytn.net'))
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
  server_socket.bind(('', 5555)) # localhost:5555
  server_socket.listen()

  fee_delegator = Account.from_key(“fee payer private key”)
  while True:
    client_socket, client_addr = server_socket.accept()
    raw_tx_str = client_socket.recv(1024)
    print("[{}] received tx : {}".format(client_addr,raw_tx_str))

    raw_tx_bytes = HexBytes.fromhex(raw_tx_str.decode().lstrip("0x"))
    feepayer_signed_tx = Account.sign_transaction_as_feepayer(
      raw_tx_bytes, fee_delegator.address, fee_delegator.key
    )

    decoded_tx = Account.decode_transaction(feepayer_signed_tx.rawTransaction)
    print("\ndecoded transaction:", to_pretty(decoded_tx))

    tx_hash = w3.eth.send_raw_transaction(feepayer_signed_tx.rawTransaction)
    tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    client_socket.send(tx_hash.encode())

    client_socket.close()

Beyond simplifying new user onboarding by paying their gas fees for them, gas fee delegation can also be used for more application scenarios. For example, such as creating a personal payer server for your newly-created blockchain accounts to execute transactions, adding support for this feature in a wallet DApp, and more.

Using AccountStore for wallet developers with web3py-ext

Before we delve into AccountStore, let’s briefly go through the key types in Klaytn. Klaytn has added features to the Ethereum account system to enhance usability and security, including allowing keys to be repurposed. As shown in the figure below, the key Private Key A, which is coupled to Address A of a legacy account, can be decoupled by changing it to Private Key B by performing an AccountUpdate transaction. With this feature, you will be able to manage multiple addresses with one private key and change the account to Multisig or Role-based.

AccountStore is a data structure that matches the private keys you currently have locally with your Klaytn account. In Klaytn, you can change the key of your account using the AccountUpdate transaction and change it to a role-based key or a Weighted-Multisig key, so you can find out which account actually contains your private key through an account search. web3klaytn provides a data structure called AccountStore to conveniently search account information.

For example, let’s look at updating a Legacy Account to a Multisig Account as shown in the figure above. User A has Private Key A and agrees with User B to create and operate a [Multisig Account]. If the account address of the Multisig Account is Address A and the transaction needs to be signed with both Private Key A and Private Key B to be executed, then Address A is no longer an address controlled by Private Key A alone. At this point, User A, who only has Private Key A, can use AccountStore to retrieve the current account status for Address A.

from web3py_ext import extend
from web3py_ext.klaytn_account.account_store import AccountStore
from eth_account import Account
from web3py_ext.utils.klaytn_utils import to_pretty
from web3 import Web3

w3 = Web3(Web3.HTTPProvider('https://public-en-baobab.klaytn.net'))

acc_list = [
    Account.from_key("Private Key A"),
    # Account.from_key_pair("Address A", "Private Key A"),
]

def account_store_test():
    a = AccountStore()
    a.refresh(w3, acc_list)
    print(to_pretty(a.get_account_infos()))

account_store_test()

In the web3py-ext example Python code above, a list of private keys or (address, private key) pairs is made and put into the refresh function of the AccountStore object, then the get_account_infos function is called to retrieve the account information from the Klaytn network, as shown in the output below.

{ 
    address: Address A, 
    nonce: 12, 
    balance: '0x0', 
    key: { 
          type: 4, 
          key: { 
                threshold: 2, 
                keys: [ 
                      { 
                          weight: 1, 
                          pubkey: { 
                                compressed: ...pubkey A..., 
                                hashed: [Address A], 
                                hasPrivateKey: true 
                          } 
                      }, 
                      { 
                          weight: 1, 
                          pubkey: { 
                                compressed: ...pubkey B..., 
                                hashed: [Address B], 
                                hasPrivateKey: false
                          } 
                      }, 
               ] 
         } 
    } 
},

While the account lookup results give you a full picture of your current account, they also separate the keys you have from those held by other users with the hasPrivateKey field.

Conclusion

This was a brief introduction to web3klaytn, a set of four SDKs for three languages, and how it allows developers to easily use Klaytn’s specialized features without much change from the Ethereum-based web3 SDKs that we are all familiar with. As of the time of this article, web3klaytn is released as a public beta and we will continue to actively develop it to deliver a smooth developer experience on Klaytn.

The release of web3klaytn SDK brings us another step closer to achieving seamless builder onboarding, which is one of the strategic goals of the ‘Klaytn as a Collective’ pillar in the Mass Adoption Trifecta as detailed in our 2023 Vision Map.

To learn more about web3klaytn, please visit the SDK’s Github or Docs, and if you have any questions, feel free to ask away in the #dev-support channel on our Discord, or in the Klaytn Developer Forum.