caver-js

이현정·2020년 11월 13일

개발팀

목록 보기
2/4

caver-js란?😮

개발자가 HTTP 또는 웹소켓 연결을 사용하여 Klaytn 노드와 상호작용할 수 있도록 하는 자바스크립트 API 라이브러리

https://ko.docs.klaytn.com/bapp/sdk/caver-js/getting-started#smart-contract

caver-js의 역할

  • HTTP 및 웹소켓을 통한 Klaytn의 JSON-RPC 클라이언트 * API의 완전한 구현
  • Klaytn 트랜잭션, 계정 및 계정 키 유형 지원
  • Klaytn 네트워크에서 스마트 컨트랙트를 배포하고 실행하기위한 자바스크립트 스마트 컨트랙트 패키지
  • Klaytn 계정 관리를 위한 인메모리 지갑
  • 수수료 위임 지원
  • Klaytn Wallet 키 형식 지원
  • RLP에서 트랜잭션 오브젝트의 인코딩/디코딩
  • 트랜잭션 객체의 서명
  • web3-js 애플리케이션을 caver-js로 쉽게 포팅

caver-js의 오류 코드 개선

caver-js는 Klaytn에서 오류 메시지를 포착할 수 있음
트랜잭션 영수증의 txError에서 찾을 수 있음

Klaytn에 트랜잭션 보낼 때 주의사항

고정된 가스 가격만큼을 제출해야 함


caver-js 입문

시작 전 준비사항!

  • 의존성 다음 패키지가 꼭 필요
    • Node.js
    • npm
    • gcc-c++
    • Solidity compiler
  • 설치
$ npm install caver-js

이 때, package.json파일이 설치 경로와 동일한 곳에 존재해야 함

👉 KLAY 전송 알아보기

keystore file을 이용해 KLAY전송

const fs = require('fs')
const Caver = require('./index')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

async function testFunction() {
    // Read keystore json file
    const keystore = fs.readFileSync('./keystore.json', 'utf8')

    // Decrypt keystore
    const keyring = caver.wallet.keyring.decrypt(keystore, 'password')
    console.log(keyring)

    // Add to caver.wallet
    caver.wallet.add(keyring)

    // Create value transfer transaction
    const vt = new caver.transaction.valueTransfer({
        from: keyring.address,
        to: '0x8084fed6b1847448c24692470fc3b2ed87f9eb47',
        value: caver.utils.toPeb(1, 'KLAY'),
        gas: 25000,
    })

    // Sign to the transaction
    const signed = await caver.wallet.sign(keyring.address, vt)

    // Send transaction to the Klaytn blockchain platform (Klaytn)
    const receipt = await caver.rpc.klay.sendRawTransaction(signed)
    console.log(receipt)
}

testFunction()

caver-js 시작하기

1. 파일 생성

<코드 입력>

$ touch test.js

작업 디렉토리에 생성된 test.js 파일을 확인해보기

<test.js에 코드를 작성>

// test.js
const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

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

testFunction()

<코드 실행 결과>

$ node ./test.js
Klaytn/v1.4.0/linux-amd64/go1.14.1
2. Klaytn 노드에 접속하기

<caver-js 모듈을 가져와 Baobab 테스트넷의 Klaytn 노드에 연결>

const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

만약 EN을 실행중이라면?

const Caver = require('caver-js')
const caver = new Caver('http://localhost:8551/')

Keyring

Keyring이란?

Klaytn 계정 주소와 개인 키가 포함된 structure

<Keyring 종류>
1. SingleKeyring : 하나의 주소와 하나의 개인 키를 저장
2. MultipleKeyring : 하나의 주소와 여러 개인 키를 저장
3. RoleBasedKeyring : 각 역할에 대해 하나의 주소와 하나 이상의 개인 키를 저장

Keyring 생성
  • SingleKeyring 생성
    개인키와 계정 주소로 SingleKeyring 생성하기
    여러 개인키로 MultipleKeyring 생성
    • 개인키로 RoleBasedKeyring 생성하기
caver-js에 Keyring 추가

지갑(caver.walle)에 Keyring 추가하기

// test.js
const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

async function testFunction() {
    // Using a keyring instance
    const keyring = caver.wallet.keyring.generate()
    caver.wallet.add(keyring)
    console.log(caver.wallet.getKeyring(keyring.address))

    // Using a keystore file
    const decrypted = caver.wallet.keyring.decrypt({ 
        version: 4,
        id: '9c12de05-0153-41c7-a8b7-849472eb5de7',
        address: '0xc02cec4d0346bf4124deeb55c5216a4138a40a8c',
        keyring: [
            { 
                ciphertext: 'eacf496cea5e80eca291251b3743bf93cdbcf7072efc3a74efeaf518e2796b15',
                cipherparams: { iv: 'd688a4319342e872cefcf51aef3ec2da' },
                cipher: 'aes-128-ctr',
                kdf: 'scrypt',
                kdfparams: {
                    dklen: 32,
                    salt: 'c3cee502c7157e0faa42386c6d666116ffcdf093c345166c502e23bc34e6ba40',
                    n: 4096,
                    r: 8,
                    p: 1
                },
                mac: '4b49574f3d3356fa0d04f73e07d5a2a6bbfdd185bedfa31f37f347bc98f2ef26'
            }
        ]
    }, 'password')

    caver.wallet.add(decrypted)
    console.log(caver.wallet.getKeyring(decrypted.address))
}

testFunction()

트랜잭션 전송

<Baobab 네트워크에서 caver-js를 사용하여 KLAY 보내기>

  • Baobab Faucet을 통해 KLAY 받기

개인키 또는 키스토어 파일을 사용하여 Klaytn Wallet에 로그인,
faucet을 통해 Baobab 테스트넷 KLAY를 받아옴

  • 송금 트랜잭션 전송

caver-js 지갑을 통해 트랜잭션 서명 가능

  1. 트랜잭션 서명하기
    caver.wallet 또는 caver.wallet.sign 사용
    caver.wallet에 Keyring을 추가하지 않고 따로 관리할 경우, transaction.sign 사용

    <서명 방법>
    Keyring을 caver.wallet에 추가하고, 트랜잭션을 생성하고,
    caver.wallet.sign를 통해 이 트랜잭션에 서명

// test.js
const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

async function testFunction() {
    // Add a keyring to caver.wallet
    const keyring = caver.wallet.keyring.createFromPrivateKey('0x{private key}')
    caver.wallet.add(keyring)

    // Create a value transfer transaction
    const valueTransfer = new caver.transaction.valueTransfer({
        from: keyring.address,
        to: '0x176ff0344de49c04be577a3512b6991507647f72',
        value: 1,
        gas: 30000,
    })

    // Sign the transaction via caver.wallet.sign
    await caver.wallet.sign(keyring.address, valueTransfer)

    const rlpEncoded = valueTransfer.getRLPEncoding()
    console.log(`RLP-encoded string: ${rlpEncoded}`)
}

testFunction()
<코드 실행 결과>
RLP-encoded string: 0x08f87e808505d21dba0082753094176ff0344de49c04be577a3512b6991507647f720194ade4883d092e2a972d70637ca7de9ab5166894a2f847f845824e44a0e1ec99789157e5cb6bc691935c204a23aaa3dc049efafca106992a5d5db2d179a0511c421d5e508fdb335b6048ca7aa84560a53a5881d531644ff178b6aa4c0a41
  1. RLP 인코딩된 서명된 트랜잭션을 caver.rpc.klay.sendRawTransaction을 통해 Klaytn에 전송

0x{RLP-encoded string}를 위 rlpEncoded 값으로 대체하여 아래 코드 실행

// test.js
const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

async function testFunction() {
    const rlpEncoding = `0x{RLP-encoded string}`

    // Send the transaction using `caver.rpc.klay.sendRawTransaction`.
    const receipt = await caver.rpc.klay.sendRawTransaction(rlpEncoding)
    console.log(receipt)
}

testFunction()

<코드 실행 결과>

$ node ./test.js
{ 
    blockHash: '0xd20066b448da77a41a46fbf0856792b85b60c42213126f661f6434b5b1263072',
    blockNumber: '0x1efb',
    contractAddress: null,
    from: '0x09a08f2289d3eb3499868908f1c84fd9523fe11b',
    gas: '0x7530',
    ...
    signatures: [
        { 
            V: '0x4e43',
            R: '0x5737aa8c88f019a3ee184faed6d34d103f77773bd5434cb0328c11738c8d9755',
            S: '0x578b118f4400999e5232bd0860cfbdbf89622f6e11cc6bd9722a86767d2723b7'
        }
    ],
    status: '0x1',
    to: '0x176ff0344de49c04be577a3512b6991507647f72',
    transactionHash: '0x43e8ab1a2365ad598448b4402c1cfce6a71b3a103fce3a69905613e50b978113',
    transactionIndex: 0,
    type: 'TxTypeValueTransfer',
    typeInt: 8,
    value: '0x1'
}

caver.wallet없이 전송도 가능

  • 영수증 확인

프로미스(promise)와 이벤트 이미터(event emitter)를 통해 트랜잭션을 전송한 결과를 가져올 수 있음

트랜잭션의 실행 결과는 영수증의 status를 통하여 확인 가능


다른 트랜잭션 타입 실행

트랜잭션 수수료 위임

수수료 위임에는 발신자 서명, 수수료 납부자 서명이 필요함.

  1. 트랜잭션 전송자가 RLP 인코딩된 트랜잭션을 만듦
// test.js
const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

async function testFunction() {
    const sender = caver.wallet.keyring.createFromPrivateKey('0x{private key}')
    caver.wallet.add(sender)

    const feeDelegatedTx = new caver.transaction.feeDelegatedValueTransfer({
        from: sender.address,
        to: '0x176ff0344de49c04be577a3512b6991507647f72',
        value: 5,
        gas: 50000,
    })

    await caver.wallet.sign(sender.address, feeDelegatedTx)

    const rlpEncoded = feeDelegatedTx.getRLPEncoding()
    console.log(rlpEncoded)
}

testFunction()

if caver.wallet에 수수료 납부자 키도 같이 있음 - caver.wallet.signAsFeePayer(feePayer.address, feeDelegatedTx)를 호출하여 수수료 납부자 서명을 feeDelegatedTx에 넣음

else - 수수료 납부자는 트랜잭션 발신자가 서명한 RLP 인코딩된 문자열에서 feeDelegatedTx를 새로 만들고, 자신의 서명을 여기에 추가

// test.js
const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

async function testFunction() {
    const feePayer = caver.wallet.keyring.createFromPrivateKey('0x{private key}')
    caver.wallet.add(feePayer)

    const rlpEncoded = '0x{RLP-encoded string}'

    const feeDelegateTxFromRLPEncoding = new caver.transaction.feeDelegatedValueTransfer(rlpEncoded)

    // Set the fee payer address.
    feeDelegateTxFromRLPEncoding.feePayer = feePayer.address
    await caver.wallet.signAsFeePayer(feePayer.address, feeDelegateTxFromRLPEncoding)

    console.log(feeDelegateTxFromRLPEncoding.getRLPEncoding())
}

testFunction()
  1. 두 서명이 완료되면, 트랜잭션을 전송
계정 업데이트
  1. 새 개인 키를 준비하여 새로운 Keyring을 만듦
  2. 필요한 유형의 Keyring을 생성
  3. 새 Keyring에서 Account 인스턴스를 생성
  4. Account 인스턴스를 입력 파라미터로 받는 AccountUpdate 트랜잭션을 Klaytn에 전송
  5. 기존 Keyring을 2번째 단계에서 만들었던 새 Keyring으로 교체
스마트 컨트랙트

저수준 ABI(Application Binary Interface)가 주어지면 스마트 컨트랙트의 모든 메소드를 자동으로 자바스크립트 호출로 변환하기 때문에, 스마트 컨트랙트가 자바스크립트 개체인 것처럼 사용됨

  1. 컨트랙트 인스턴스(contractInstance.options.address)에 스마트 컨트랙트 주소를 입력.
  2. 바이트코드를 data필드에 전달해 배포
  3. Keyring을 이용해 트랜잭션 서명
서명하는 사람이 여럿일 때, 서명을 수집하고 트랜잭션 전송하기
  • 순차적 서명
  1. 같은 트랜잭션을 공유할 때

    여러 개인 키를 사용하여 트랜잭션에 순차적으로 서명

  2. 같은 트랜잭션을 공유하지 않을 때

    서명을 RLP 인코딩된 문자열에 첨부
    서명된 raw transaction들을 결합( RLP 인코딩된 트랜잭션들을 하나로 통합하 고 전송)

// test.js
const Caver = require('caver-js')
const caver = new Caver('https://api.baobab.klaytn.net:8651/')

async function testFunction() {
    const vt = new caver.transaction.valueTransfer({
        from: '0x0fa355263f37f5a54d9179452baa8b63b8b2cdde',
        to: '0x45c2a1e3a1c3957a06dae73ad516461c2d2c7ccc',
        value: 1,
        gas: 70000,
    })
    const rlpEncodedStrings = [
        '0x08f87f018505d21dba00830111709445c2a1e3a1c3957a06dae73ad516461c2d2c7ccc01940fa355263f37f5a54d9179452baa8b63b8b2cddef847f845824e44a01aa72b883ca540c8a63de244cd061ec4f9efb139541e8db304c07ec27bc9d272a06a4ca54f6269f2ddfe3648eb9ed57b0c5739f0077e1a38449f3ae3cc0b20dc3e',
        '0x08f8c6018505d21dba00830111709445c2a1e3a1c3957a06dae73ad516461c2d2c7ccc01940fa355263f37f5a54d9179452baa8b63b8b2cddef88ef845824e44a01aa72b883ca540c8a63de244cd061ec4f9efb139541e8db304c07ec27bc9d272a06a4ca54f6269f2ddfe3648eb9ed57b0c5739f0077e1a38449f3ae3cc0b20dc3ef845824e43a0fd76dfc53c812ec6aa860076f731e3913936088a1518cc34f2d176bcbe0ac772a071491c938458fffe106dde485fc8b26cbebe8a517c46bd185b126930f480d773',
        '0x08f8c6018505d21dba00830111709445c2a1e3a1c3957a06dae73ad516461c2d2c7ccc01940fa355263f37f5a54d9179452baa8b63b8b2cddef88ef845824e44a01aa72b883ca540c8a63de244cd061ec4f9efb139541e8db304c07ec27bc9d272a06a4ca54f6269f2ddfe3648eb9ed57b0c5739f0077e1a38449f3ae3cc0b20dc3ef845824e43a021e84a4740b374cdcf0cc38f93225f6d2f77388a9d90302d47b4f3ed84e4db5fa072ff5e77d2506d5222081c4d2a341c6ee5d258500030564f985951472f247b7d',
    ]
    const combined = vt.combineSignedRawTransactions(rlpEncodedStrings)
    console.log(combined)
}

testFunction()

0개의 댓글