Technology, Tutorials, for Developer, Tool support

web3klaytn SDK 사용해보기

web3klaytn은 Klaytn 메인넷과 테스트넷인 Cypress와 Baobob에서 정보를 쉽게 얻고, 트랜잭션을 쉽게 보낼 수 있도록 도와주는 Klaytn만을 위한 Software Development Kit(SDK)입니다. 기존에 사용되던 이더리움 기반의 SDK(ethersjs, web3js, web3j, web3py)는 사용 패턴이 Klaytn 메인넷에 최적화되지 않아, 사용자들이 사용에 어려움을 겪거나 Klaytn 체인의 고유한 차별화 기능인 트랜잭션 수수료 대납, 멀티시그 키, 역할기반 키 등을 활용하기가 어려웠습니다. 

이번에 릴리즈된 web3klaytn은 이더리움을 기반의 SDK 서비스에 익숙한 개발자들을 심리스하게 온보딩하면서도, Klaytn만을 위한 추가 기능을 자유롭게 사용할 수 있게됩니다. 

Klaytn은 2023 비전맵을 발표하며, ‘Klaytn is as Collective’라는 큰 줄기를 위한 전략적인 목표의 하나로 Seamless Builder On-Boarding를 실현하겠다고 밝힌 바 있습니다. web3klaytn의 릴리즈는 이런 로드맵 이행의 주요 마일스톤 중 하나로, Klaytn 메인넷의 타 체인과의 호환성을 강화하는 한편, Klaytn만의 특화 기능을 제공함으로써 Klaytn 체인 상에 더욱 많은 Dapp들이 만들어져 더욱 견고한 생태계로 나아갈 수 있는 중요한 통로가 될 것입니다. 이번 듀토리얼에서는 web3klaytn을 어떻게 사용하는지 자세히 알아보겠습니다. 

지금, web3klaytn SDK github 에서 바로 만나볼 수 있습니다. 


소개

기존 caver-js/java SDK가 있었음에도, 불구하고 web3klaytn을 개발한 배경은 다음과 같습니다.

  • Seamless development: 이더리움계열 체인의 서비스 개발자들이 쉽게 클레이튼 사용
    • 이더리움 계열에서 서비스를 운영하던 개발자들에게 Klaytn만의 빠른 속도와 응답성 등은 큰 매력점입니다. 이에 클레이튼에도 서비스를 출시하려는 시도가 이루어지고 있는 와중에,  Klaytn에 특화된 SDK를 사용할 수 있다면 개발자들이 보다 쉽게 온보딩할 수 있어 클레이튼의 생태계에 더욱 많은 서비스가 생겨날 수 있을 것입니다.
  • Hardhat integration: hardhat에서 web3klaytn을 통해 클레이튼만의 특화된 기능 사용
    •  서비스 개발자들은 컨트랙트를 작성하며 테스트하는데 많이 사용하는 hardhat은 기본적으로 ethers.js를 사용하므로, 여기에 web3klaytn의 ethers.js버전 ethers-ext를 추가하여 클레이튼의 특화된 기능을 사용 할 수 있습니다.
  • Multichain development:  지갑과 브릿지 등 멀티체인 서비스를 개발할 때 같은 SDK사용
    •  블록체인 전체 생태계가 커지면서 멀티체인 서비스도 빠르게 성장하고 있습니다. 이 때 한 체인에서 개발할 때 사용한 SDK를 다른 체인에서 사용 할 수 있게 된다면 개발 생산성 및 유지보수가 쉬워지며 결국 서비스에 더욱 집중 할 수 있는 환경을 제공하므로 생태계 확장에 기여할 수 있습니다. 

web3klaytn

web3klaytn은 기존의 이더리움 호환 web3 SDK들에 클레이튼 관련 기능을 추가해주는 라이브러리 확장(extension)의 총 집합입니다. Web3 개발에 주로 사용되는 4개의 라이브러리를 위한 4개의 extension을 제공합니다. 라이브러리 확장은 기존 라이브러리의 기능을 방해하거나 대체하지 않고 클레이튼의 고유한 기능을 활용할 수 있도록 도와줍니다.

현재 0.9.1-beta 버전으로 릴리즈 되어있으며 바로 사용해보실 수 있습니다.

그렇다면, web3klaytn을 통해 얼마나 쉽고 다양한 기능을 만나볼 수 있는지 더욱 자세히 알아보겠습니다. 

ethers-ext로 Klaytn타입 트랜잭션 보내기

클레이튼은 legacy 트랜잭션 타입도 지원하므로 이를 통해서도 KLAY를 전송 할 수 있지만, value-transfer 트랜잭션 타입도 존재합니다. 단 2줄 추가로 클레이튼 타입 트랜잭션을 만들어 전송 할 수 있습니다.

// 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);

@JavaScript

이외에도 클레이튼은 가스비 대납 (fee delegation), 계정의 개인키 변경 (account update) 등 다양한 트랜잭션 타입을 지원하여 용도에 맞게 사용할 수 있습니다. ethers-ext를 활용하면 트랜잭션 오브젝트에 “type” 필드와 트랜잭션 타입에 맞는 필드를 추가하기만 하면 기존 ethers.js 코드에 약간의 추가만으로 클레이튼 트랜잭션 타입을 활용할 수 있습니다. 다른 타입에 대한 예제는 ethers-ext GitHub에서 확인할 수 있습니다. 

web3py-ext로 대납서버 만들기

클레이튼의 트랜잭션 가스비 대납(fee delegation) 기능은 서비스 개발자들로부터 가장 큰 호응을 얻었던 기능 중 하나입니다. 대납 기능을 사용하면 신규 유저가 지갑에 KLAY가 없어도 web3와 dapp 경험을 시작하도록 할 수 있습니다. 가스비 대납은 일반적으로 유저가 클라이언트 측에서 트랜잭션을 한 번 서명하면 대납 서버가 받아서 대납 계정으로 트랜잭션을 추가 서명하여 네트워크에 전송하는 방식으로 이루어집니다다. web3py-ext를 이용하면 간단한 가스비 대납 클라이언트와 서버를 만들 수 있습니다.

# 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())

@Python

“from web3py_ext import extend” 구문은 기존 web3.py에 클레이튼을 위한 기능을 확장합니다. Web3py-ext가 제공하는 empty_tx와 fill_transaction 함수는 트랜잭션 타입에 맞게 필드들을 채우는 함수입니다. 유저는 트랜잭션 내용을 입력한 뒤 자신의 개인키로 트랜잭션을 서명합니다. 그리고 서명한 트랜잭션을 클레이튼 네트워크가 아닌 대납 서버로 전송합니다.

# 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()

@Python

대납 서버는 사용자가 서명한 트랜잭션을 받아서 대납자 서명을 추가한 뒤 클레이튼 네트워크로 전송합니다. 전송된 트랜잭션의 가스비는 대납자 계정이 부담하게 됩니다. 한편, 이런 대납 기능을 기반으로 더 많은 응용 시나리오를 만들어볼 수 있습니다. 예컨대, 새로운 블록체인 계정을 만들 때 다음 트랜잭션을 실행하기 위한 수수료로 1 KLAY를 전송해본 경험이 있다면, 위의 예제를 응용하여 개인용 대납서버를 만들어 보거나, 지갑에서 이런 대납 기능을 지원할 수 있는 등 더욱 유용하고 다양한 시나리오가 나오기를 기대해볼 수 있습니다.  

web3py-ext로 지갑 개발자를 위한 AccountStore사용하기

AccountStore에 대해 이야기하기 전에,클레이튼의 키타입에 대해서 간략하게 설명하겠습니다 클레이튼은 이더리움 계정체계에 사용성과 보안성을 강화할 수 있도록 기능을 추가하였고, 이에따라 키를 용도에 맞게 변경 할 수 있습니다. 아래 그림과 같이 Legacy Account의 Address A에 Coupling되어 있는 키 Private Key A를 Account Update 트랜잭션을 수행하여 Private Key B로 변경 하여 decoupling할 수 있습니다. 이를 응용하면 여러개의 주소 Address A, B, C를 하나의 Private Key로 관리 할 수도 있게 됩니다. 이 외에도 Multisig, Role-based와 같은 Account로도 변경 가능합니다.

AccountStore는 현재 로컬에 가지고 있는 개인키들을 클레이튼 계정과 매칭한 자료구조입니다. 클레이튼에서는 Account Update 트랜잭션을 이용해서 계정의 키를 변경하여, Role-based 키나 Weighted-Multisig 키로 변경이 가능하므로 실제 나의 개인키가 어떤 계정에 포함되어 있는지는 계정 조회를 통해 알 수 있습니다. web3klaytn에서는 AccountStore라는 자료구조를 제공하여 계정 정보를 편리하게 조회할 수 있습니다.

예시로, 위 그림과 같이 기존의 [Legacy Account]를 [Multisig Account]로 업데이트한 경우를 살펴보겠습니다. 사용자 A는 Private Key A를 가지고 있고 사용자 B와 [Multisig Account]를 만들어 운영하자고 합의하였습니다. [Multisig Account]의 계정주소는 Address A로 하고 Private Key A, Private Key B로 모두 서명해야 트랜잭션이 실행되도록 하자고 하였을 때, Address A는 더이상 Private Key A만으로 컨트롤되는 주소가 아니게 됩니다. 이때 Private Key A만을 가지고 있는 User A는 현재 Address A에 대한 계정상태를 알기위해 AccountStore를 사용 할 수 있습니다.

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()

@Python

위의 web3py-ext 예제에서는 내가 가지고 있는 개인키 또는 (주소, 개인키) 쌍을 리스트로 만들어 AccountStore 객체의 refresh함수에 넣어준 다음에 get_account_infos함수를 호출하여 아래와 같이 클레이튼 네트워크로부터 계정정보를 조회했습니다.

{ 
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
} 
}, 
] 
} 
} 
},

@Unset

계정 조회 결과에는 현재 계정에 대한 전체적인 정보도 있지만, 내가 가지고 있는 키와 다른 사용자가 가지고 있는 키를 hasPrivateKey필드로 구분해주고 있습니다.

결론

지금까지 web3klaytn에 대한 간략한 소개와 이를 통해 어떠한 것들을 할 수 있는지 간략하게 알아보았습니다. web3klaytn은 3가지 언어를 위한 4가지  SDK의 집합으로, 개발자들에게 익숙한 web3 SDK에서 큰 변화없이 심리스하게 클레이튼의 특화 기능을 사용할 수 있다는 점을 예제를 통해 확인해보았습니다.  지금도 활발하게 개발을 진행하고 있으며 약 1달간의 베타기간을 거치고 있습니다. 많은 관심과 피드백을 부탁드리며, 오픈소스를 활용해 더 많은 서비스와 dapp이 개발된다면 ‘블록체인으로 더 나은 세상’을 만들고자 하는 클레이튼의 방향성에 큰 기여가 될 것으로 기대합니다.   


이 튜토리얼보다 자세한 내용은 web3klaytn Github또는 Docs를 참고하세요. 또한 궁금하신 내용은 디스코드 “개발자-지원” 또는 Klaytn Developer Forum을 통해 질문주시면 답변드리겠습니다.