Caver-js UseCase

Jeongmin Yeo (Ethan)·2020년 11월 13일
6

클레이튼

목록 보기
2/2
post-thumbnail

Klaytn Blockchain 에서 사용하는 Caver SDK인 caver-js에 대해서 공부해보고 다양한 use cases들을 소개하겠습니다.

caver-js

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

아래에 있는 Klaytn Reference를 참고하면서 정리했습니다.

https://docs.klaytn.com/bapp/sdk/caver-js


Glossary

caver-js를 공부하면서 자주 등장하는 헷갈리기 쉬운 용어부터 먼저 정리하겠습니다.

  • Address
    • Klaytn Blockchain은 Address 기반입니다. 20-byte의 address는 Account를 구별해주는 값입니다.

  • Account

    • Klaytn의 Account는 Person Balance와 Smart Contract까지 포함하는 Data Structure입니다.

    • 크게 두 종류가 있습니다.

      • EOA (Externally Owned Accounts), SCAs (Smart Contract Accounts)
    • EOA (Externally Owned Accounts)란 무엇인가?

      • EOA는 오로지 privateKey에 의해 제어됩니다. 그리고 privateKey에 의해 Key Pairs가 생성됩니다.
      • Balance나 Nonce 같은 정보를 포함하고 있습니다.
      • 이 Account에는 Code나 Storage를 포함하고 있지 않습니다.
      • Attributes
        • type: Account의 타입을 나타내는 필드 값입니다. EOA 인 경우에는 0x1, SCAs인 경우에는 0x2가 들어갑니다.
        • nonce: Transaction의 순서를 결정하는 number 입니다.
        • balance: 이 계정이 소유하고 있는 Klay를 말합니다.
        • humanReadable: Account가 HRA(Human readable address)인지 아닌지 설명해주는 값입니다.
        • key: Klaytn의 계정은 키와 Account가 분리되어있습니다. 이 필드의 값에는 여러 종류의 키(AccountKey)가 올 수 있습니다
          • ex) AccountKeyLegacy, AccountKeyPublic, AccountKeyFail, AccountKeyWeightedMultisig, AccountKeyRoleBased
    • SCAs (Smart Contract Accounts)란 무엇인가?

      • SCAs란 Smart Contract Accounts로 작성한 Code에 의해 제어됩니다.
      • SCA는 자기 혼자서 자체적으로 Transaction을 만들어 낼 수 없습니다. 즉 누군가 이 컨트랙트를 실행시켜줘야 합니다.
      • Attributes
        • type: Account의 타입을 나타내는 필드 값입니다. EOA 인 경우에는 0x1, SCAs인 경우에는 0x2가 들어갑니다.
        • nonce: Transaction의 순서를 결정하는 number 입니다.
        • balance: 계정이 가지고 있는 Klay 양입니다.
        • humanReadable: Account가 HRA(Human readable address)인지 아닌지 설명해주는 값입니다.
        • key: Klaytn의 계정은 키와 Account가 분리되어있습니다. 이 필드의 값에는 여러 종류의 키(AccountKey)가 올 수 있습니다
          • ex) AccountKeyLegacy, AccountKeyPublic, AccountKeyFail, AccountKeyWeightedMultisig, AccountKeyRoleBased
        • codeHash: 스마트 컨트랙트의 고유 해시값입니다. 처음 컨트랙트가 생성될 때 설정됩니다.
        • storageRoot: 계정에 저장된 모든 변수들의 값을 포함하는 Merkle Patricia trie 루트의 256비트 해시입니다.
        • codeFormat: 이 계정의 코드 형식입니다. 현재는 EVM(0x00)만 지원합니다.

  • AccountKey

    • Account는 다양한 Key와 연결해서 사용할 수 있습니다.

    • 트랜잭션 검증에 사용됩니다.

      1. ecrecover(txhash, txsig)로부터 파생된 공개키를 얻습니다.
      2. 파생된 공개키가 해당 계정의 공개키와 같은지 확인합니다.
    • AccountKeyNil

      • AccountKeyNil은 빈(empty) 키를 나타냅니다. 계정이 AccountKeyNil object를 가지려고 하면 트랜잭션은 실패합니다.
    • AccountKeyLegacy

      • AccountKeyLegacy는 해당 키 쌍에서 파생된 주소를 가진 계정에 사용됩니다
    • AccountKeyPublic

      • AccountKeyPublic은 공개키를 하나 가진 계정에 사용됩니다.
    • AccountKeyFail

      • 계정에 AccountKeyFail 키가 있으면 트랜잭션 유효성 검증 프로세스는 항상 실패하게 됩니다. 특정 스마트 컨트랙트 계정에서 전송된 트랜잭션이 항상 실패하도록 사용될 수 있습니다.
    • AccountKeyWeightedMultiSig

      • AccountKeyWeightedMultiSig는 계정 키 타입입니다. 여기에는 threshold와 WeightedPublicKeys가 저장되어 있습니다. WeightedPublicKeys는 공개키와 공개키의 가중치(weight)로 이루어진 리스트입니다.
    • AccountKeyRoleBased

      • AccountKeyRoleBased는 역할기반 키를 의미합니다
      • AccountKeyRoleBased 키는 세 종류가 있습니다. RoleTransaction, RoleAccountUpdate, RoleFeePayer
        • RoleTransaction: Index 0. 기본키. TxTypeAccountUpdate 이외의 트랜잭션은 이 역할의 키로 서명해야합니다.
        • RoleAccountUpdate: Index 1. TxTypeAccountUpdate 트랜잭션은 이 키로 서명되어야 합니다
        • RoleFeePayer: Index 2. 이 계정이 발신자 대신 트랜잭션 수수료를 보내려면 이 키로 트랜잭션에 서명해야합니다.

  • Keyring
    • Keyring은 Account와 privateKey를 포함하는 Data Structure 입니다.
    • Keyring의 종류는 세가지가 있습니다.
      • SingleKeyring : one Address, one PrivateKey
      • MultipleKeyring: one Address, Multiple PrivateKey
      • RoleBasedKeyring: one Address, one or more PrivateKey for each role.
    • 왜 Keyring이라는 Data Structure이 있는가? 목적은 Transaction에 Sign을 하기 위해서

  • Wallet
    • Wallet은 Keyring을 관리하기 위한 in-memory 형태의 지갑입니다.
    • Keyring을 지갑에 추가하거나, 지우거나, 지갑안에 있는 Keyring을 통해서 Sign 할 수 있습니다.

Caver Use Case

1. Installation

$ npm install caver-js 

or 

$ yarn add caver-js 

2. Managing Keyrings

2.1 creating a Keyring

아래와 같이 임의의 값을 사용해 SingleKeyring을 생성할 수 있습니다.

const createKeyring = () => {
   
  const keyring = caver.wallet.keyring.generate()
  console.log(keyring);
}

createKeyring(); 

// expected output
SingleKeyring {
  _address: '0x44303b192b8ecf9239b7246bab627f0b43f6426b',
  _key: PrivateKey {
    _privateKey: '0xb633f5ce69aecaa92bac53ccceaadd7d5346c6a517f565989d08c2397e5e00c2'
  }
}

2.2 Creating a MultipleKeyring with multiple private keys

caver를 통해서 MultipleKeyring을 만들 수 있습니다.

const address1 = '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab';
const privateKey1 = '0x43e38109a5d25cd79fc6928a60e4a8749120915e7e2df2d2971ca0b571e98fbb';

const address2 = '0xe443ff0c67ef9e2e44fe758592a8e5b46708dd0f';
const privateKey2 = '0xd678d994df0b8ecd94903b20b3a70e473830c5d43dbb9d58ada0a751f50bee00';

const createMultipleKeyringTest = async () => {
    // Create a keyring with an address and private keys
    const keyring = caver.wallet.keyring.createWithMultipleKey(address1, [ privateKey1, privateKey2]);

    console.log(keyring)
}

createMultipleKeyringTest(); 

// expected output
MultipleKeyring {
  _address: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  _keys: [
    PrivateKey {
      _privateKey: '0x43e38109a5d25cd79fc6928a60e4a8749120915e7e2df2d2971ca0b571e98fbb'
    },
    PrivateKey {
      _privateKey: '0xd678d994df0b8ecd94903b20b3a70e473830c5d43dbb9d58ada0a751f50bee00'
    }
  ]
}

궁금한게 caver.wallet.keyring.createWithMultipleKey을 실행 후 실제 내 계정은 AccountKeyWeightedMultiSig가 된건가 궁금했다.

static createWithMultipleKey(address, keyArray) {
  if (!isMultipleKeysFormat(keyArray))
    throw new Error(`Invalid format of parameter. 'keyArray' should be an array of private key strings.`)

  return new MultipleKeyring(address, keyArray)
}

실제 코드를 보니 데이터 형태만 Multisig Account 처럼 된 거고 내 계정이 Multisig Account로 업데이트 하는 트랜잭션을 발생 시키진 않는다.

const TransactionMultipleKeyringTest = async () => {
    // Create a keyring with an address and private keys
    const keyring = caver.wallet.keyring.createWithMultipleKey(address1, [ privateKey1, privateKey2]);

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

TransactionMultipleKeyringTest(); 

// expected output
UnhandledPromiseRejectionWarning: Error: Returned error: invalid transaction v, r, s values of the sender

3. Sending a Transaction

caver-js를 사용하여 KLAY를 보내는 방법입니다.

3.1 Getting KLAY via Baobab Faucet

개인키 또는 키스토어 파일을 사용하여 Klaytn Wallet에 로그인하고 테스트를 위해 faucet을 통해 Baobab 테스트넷 KLAY를 받습니다.

https://ko.docs.klaytn.com/bapp/developer-tools/klaytn-wallet#how-to-receive-baobab-testnet-klay

3.2 Sending a Value Transfer Transaction

트랜잭션 서명은 caver-js 지갑을 통해 할 수 있습니다. 트랜잭션을 네트워크에 보내려면 아래와 같이 2단계를 거쳐야합니다.

  1. Sign a transaction
  • caver.wallet에 Keyring이 있다면 caver.wallet.sign function을 통해서 서명 할 수 있습니다.
  • caver.wallet에 Keyring을 추가하지 않고 따로 관리한다면, transaction.sign 함수를 통해 트랜잭션에 서명할 수 있습니다.
  • caver.wallet.sign vs transaction.sign 차이는 단지 wallet을 쓰는 여부입니다.
    • caver.wallet.sign(address, transaction [, index][, hasher])
      • 매개변수인 address가 sender가 되며 wallet에 있는 Keyring 을 찾아서 서명을 합니다
      • 그 후 transaction Object에 signature 필드가 추가됩니다.
      • index (optional)는 wallet에 저장되어 있는 private Key의 순서를 말합니다.
      • hasher (optional)는 transaction Hash를 만드는 Hash function을 말합니다.
      • Account Update Transaction의 경우에 AccountKey는 roleAccountUpdateKey여야 합니다
      • 그렇지 않은 경우에는 roleTransactionKey여야 합니다.
  1. Send the RLP-encoded string of the signed transaction
  • RLP 인코딩된 서명된 트랜잭션을 caver.rpc.klay.sendRawTransaction을 통해 Klaytn에 전송합니다.
    • RLP 인코딩이란 무엇인가?
      • 네트워크에서 데이터를 주고 받기 위해 만든 통일된 형식입니다 이렇게 만드는걸 직렬화라고도 합니다
const sendTransactionTest = async () => {
    // Add a keyring to caver.wallet
    const keyring = caver.wallet.keyring.createFromPrivateKey(privateKey1);
    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}`)
}

sendTransactionTest(); 

// expected output 
RLP-encoded string: 0x08f87e028505d21dba0082753094176ff0344de49c04be577a3512b6991507647f720194d8919593e0736e953bc7c416fb2dc71fa70a15abf847f8458207f5a02647b0c3d2834f63088b0dc4af54a151c6a7e3c0b89493032ecd2a2668722c39a07a563036c3c0356df7a8de33e7a1793cc78166fea487a2f4c30eef4c086d6496

3.3 Checking Receipts

Transaction을 네트워크에 보낸 후 영수증을 받는 과정입니다.


// Using a promise - async/await
const receipt = await caver.rpc.klay.sendRawTransaction(rawTransaction)
console.log(receipt)

// Using a promise
caver.rpc.klay.sendRawTransaction(rawTransaction).then(console.log)

// expected output 
{
  blockHash: '0xcd9d7c70fbaf27dd408b2a704b5fd8d0ec2a26ce29ba05a5a5409eb258433e4a',
  blockNumber: '0x29b46d2',
  contractAddress: null,
  from: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  gas: '0x7530',
  gasPrice: '0x5d21dba00',
  gasUsed: '0x5208',
  logs: [],
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  nonce: '0x2',
  senderTxHash: '0x59572311b816ca99996f6ab165e17b1f4921d74ea14549fc8304e4ba79413f04', // 발신자만 서명한 트랜잭션 해시 입니다
  signatures: [
    {
      V: '0x7f5',
      R: '0x2647b0c3d2834f63088b0dc4af54a151c6a7e3c0b89493032ecd2a2668722c39',
      S: '0x7a563036c3c0356df7a8de33e7a1793cc78166fea487a2f4c30eef4c086d6496'
    }
  ],
  status: '0x1', // 트랜잭션이 성공적으로 실행됐다면 0x1을 반환하고 아니라면 0x0을 반환합니다. 
  to: '0x176ff0344de49c04be577a3512b6991507647f72',
  transactionHash: '0x59572311b816ca99996f6ab165e17b1f4921d74ea14549fc8304e4ba79413f04', // Fee delegation이 아니라면 senderTxHash와 항상 동일합니다. 
  transactionIndex: '0x0',
  type: 'TxTypeValueTransfer', // 트랜잭션의 유형을 나타내는 문자열입니다
  typeInt: 8, // 트랜잭션의 유형을 나타내는 정수입니다.
  value: '0x1' // peb로 전송된 값입니다
}

트랜잭션이 실패했다면 txError 필드가 추가되고 에러 메시지가 포함됩니다.


4. Executing Other Transaction Types

Klaytn은 확장성과 성능을 위한 다양한 트랜잭션을 제공합니다.

4.1 Fee Delegation

트랜잭션 전송자일 때 RLP 인코딩된 트랜잭션을 만드는 예시입니다.

const createSenderRlpEncoded = async () => {
    const sender = caver.wallet.keyring.createFromPrivateKey(privateKey1);
    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)
    return rlpEncoded;
}

createSenderRlpEncoded(); 

// expected output
0x09f884038505d21dba0082c35094176ff0344de49c04be577a3512b6991507647f720594d8919593e0736e953bc7c416fb2dc71fa70a15abf847f8458207f5a095853fe30d984e1d7b68abfd6defa701c40ce744b9e2b9d9d1197dfbdc32f8c1a071befa60a3b9c346d86570f1e1d8a3066ffd5c19766271901111cdf3908b923f80c4c3018080

Fee Deleagation Transaction은 RLP 인코딩 된 Raw Transaction에 feePayerSignatures를 첨부한 후 클레이튼으로 Transaction을 전파할 수 있습니다.

const FeeDelegationTransactionTest = async (RLPEncoded) => {
    const feePayer = caver.wallet.keyring.createFromPrivateKey(privateKey2)
    caver.wallet.add(feePayer)

    const rlpEncoded = RLPEncoded;

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

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

    const receipt = await caver.rpc.klay.sendRawTransaction(feeDelegateTxFromRLPEncoding.getRLPEncoding())
    console.log(receipt)
}

// 위에 있는 RLP 인코딩 된 값을 가지고옵니다.
FeeDelegationTransactionTest('0x09f884038505d21dba0082c35094176ff0344de49c04be577a3512b6991507647f720594d8919593e0736e953bc7c416fb2dc71fa70a15abf847f8458207f5a095853fe30d984e1d7b68abfd6defa701c40ce744b9e2b9d9d1197dfbdc32f8c1a071befa60a3b9c346d86570f1e1d8a3066ffd5c19766271901111cdf3908b923f80c4c3018080');

// expected output 
{
  blockHash: '0xfaad1e118efec913f0d60d127cd6850d0bcdb043e84736e0ab48eefe85a2ecff',
  blockNumber: '0x29b55d6',
  contractAddress: null,
  feePayer: '0xe443ff0c67ef9e2e44fe758592a8e5b46708dd0f',
  feePayerSignatures: [
    {
      V: '0x7f5',
      R: '0xddaec07c471ba3e626ba371adb06f0fea73248e603ae9971fba6fee23f0a2ca3',
      S: '0x65527a6de68d0e26b88ade532eb6dfd86cdff69f40ab04c87f9cce13628097f8'
    }
  ],
  from: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  gas: '0xc350',
  gasPrice: '0x5d21dba00',
  gasUsed: '0x7918',
  logs: [],
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  nonce: '0x3',
  senderTxHash: '0x8887d18368e77558bea5454c49c835a3e2102d4314223d440be2dd282fb8d595',
  signatures: [
    {
      V: '0x7f5',
      R: '0x95853fe30d984e1d7b68abfd6defa701c40ce744b9e2b9d9d1197dfbdc32f8c1',
      S: '0x71befa60a3b9c346d86570f1e1d8a3066ffd5c19766271901111cdf3908b923f'
    }
  ],
  status: '0x1',
  to: '0x176ff0344de49c04be577a3512b6991507647f72',
  transactionHash: '0xad9ddb142128a384ceb92dcd7108ad1cce4e3159cedd4e7905d2ab1cc9c2c0ac',
  transactionIndex: '0x0',
  type: 'TxTypeFeeDelegatedValueTransfer',
  typeInt: 9,
  value: '0x5'
}

4.2 Account Update

Klaytn Account에 있는 privateKey를 새로운 privateKey로 바꿀 수 있습니다.

주의할 점은 바꿀 때 새로운 privateKey를 준비해야 합니다.


const changeKeyringTest = async () => {
    let senderKeyring = caver.wallet.keyring.createFromPrivateKey(privateKey1)
    caver.wallet.add(senderKeyring)

    const newPrivateKey = caver.wallet.keyring.generateSingleKey()

    console.log(senderKeyring);

    console.log(`new private key string: ${newPrivateKey}`)
    const newKeyring = caver.wallet.keyring.createWithSingleKey(senderKeyring.address, newPrivateKey)

    console.log(newKeyring);
}

changeKeyringTest(); 

// expected output
SingleKeyring {
  _address: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  _key: PrivateKey {
    _privateKey: '0x43e38109a5d25cd79fc6928a60e4a8749120915e7e2df2d2971ca0b571e98fbb'
  }
}
new private key string: 0x93bf6e02927da67b4c2f562a970d17905becf086de119f9765c01620dd682f6e
SingleKeyring {
  _address: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  _key: PrivateKey {
    _privateKey: '0x93bf6e02927da67b4c2f562a970d17905becf086de119f9765c01620dd682f6e'
  }
}

새로 만들어진 Keyring을 통해서 Account Update Transaction을 낸다면 AccountKey는 새로운 privateKey로 바껴집니다.


const AccountUpdateTransactionTest = async () => {
    let sender = caver.wallet.keyring.createFromPrivateKey(privateKey1)
    caver.wallet.add(sender)

    const newPrivateKey = caver.wallet.keyring.generateSingleKey()

    console.log(sender);

    console.log(`new private key string: ${newPrivateKey}`)
    const newKeyring = caver.wallet.keyring.createWithSingleKey(sender.address, newPrivateKey)

    console.log(newKeyring);
    const account = newKeyring.toAccount()

    const updateTx = new caver.transaction.accountUpdate({
        from: sender.address,
        account: account,
        gas: 50000,
    })
    await caver.wallet.sign(sender.address, updateTx)
    const receipt = await caver.rpc.klay.sendRawTransaction(updateTx)
    console.log(receipt)

    // Update the keyring in caver.wallet for signing afterward.
    sender = caver.wallet.updateKeyring(newKeyring)

    console.log(sender);  
}

AccountUpdateTransactionTest(); 

// expected output
SingleKeyring {
  _address: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  _key: PrivateKey {
    _privateKey: '0x43e38109a5d25cd79fc6928a60e4a8749120915e7e2df2d2971ca0b571e98fbb'
  }
}
new private key string: 0x8e9028ca822129454cada528e70dbb8dad1093da3e9211ab836a02890e571335
SingleKeyring {
  _address: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  _key: PrivateKey {
    _privateKey: '0x8e9028ca822129454cada528e70dbb8dad1093da3e9211ab836a02890e571335'
  }
}
{
  blockHash: '0xf0c4cbe5563105dc40a4887559fdde94b4ac2e5b7f416a3c5461b850481d720b',
  blockNumber: '0x29b598f',
  contractAddress: null,
  from: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  gas: '0xc350',
  gasPrice: '0x5d21dba00',
  gasUsed: '0xa028',
  key: '0x02a103dcaf378c3709441439238bc17509a5299a1a3d7ab7680c6a1a24020ced40c5e1',
  logs: [],
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  nonce: '0x4',
  senderTxHash: '0x9154b367ace2d3cce7182844a391b685f98f80acce24f7466c74a038b51a48ea',
  signatures: [
    {
      V: '0x7f6',
      R: '0x8d2e928d0983bcadbec10f9b9a11527be300d1170053697c7d77ce923e5395ca',
      S: '0x446d5db22a159d751e18c6705bbef4a63e2f1775dc44607a931d6c6d94ebdd6a'
    }
  ],
  status: '0x1',
  transactionHash: '0x9154b367ace2d3cce7182844a391b685f98f80acce24f7466c74a038b51a48ea',
  transactionIndex: '0x0',
  type: 'TxTypeAccountUpdate',
  typeInt: 32
}
SingleKeyring {
  _address: '0xd8919593e0736e953bc7c416fb2dc71fa70a15ab',
  _key: PrivateKey {
    _privateKey: '0x8e9028ca822129454cada528e70dbb8dad1093da3e9211ab836a02890e571335'
  }
}

4.3 Smart Contract

Caver를 이용하면 스마트 컨트랙트의 모든 메소드를 자동으로 자바스크립트 호출로 변환합니다. 이를 통해 스마트 컨트랙트가 마치 자바스크립트 객체인 것처럼 스마트 컨트랙트와 상호작용할 수 있습니다

4.3.1 Smart Contract 작성 후 배포

Klaytn IDE로 가서 솔리디티 언어로 스마트 컨트랙트를 개발 한 후 배포합니다.

// Klaytn IDE uses solidity 0.4.24, 0.5.6 versions.
pragma solidity >=0.4.24 <=0.5.6;

contract Count {
    // Storage variable `count` (type: uint256)
    uint256 public count = 0;

    // Get current node's block number.
    function getBlockNumber() public view returns (uint256) {
      return block.number;
    }

    // Set value of storage variable `count`.
    function setCount(uint256 _count) public {
      count = _count;
    }
}

Smart Contract를 배포하고 난 뒤 contract AddressABI를 가지고 옵니다.


const contractAddress = '0x4ed05eb7ea2e2a0a8569b7d532beb2425b8c63cb';
const ABI = [
    {
        "constant": false,
        "inputs": [
            {
                "name": "_count",
                "type": "uint256"
            }
        ],
        "name": "setCount",
        "outputs": [],
        "payable": false,
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "count",
        "outputs": [
            {
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    },
    {
        "constant": true,
        "inputs": [],
        "name": "getBlockNumber",
        "outputs": [
            {
                "name": "",
                "type": "uint256"
            }
        ],
        "payable": false,
        "stateMutability": "view",
        "type": "function"
    }
]

const ContractInstanceTest = async () => {
    const keyring = caver.wallet.keyring.createFromPrivateKey(privateKey2);
    caver.wallet.add(keyring)

    const contractInstance = new caver.contract(ABI,contractAddress);

    const receipt = await contractInstance.methods.setCount(12).send({from:keyring.address,gas:'0x4bfd200'});
    const result = await contractInstance.methods.getBlockNumber().call({from:keyring.address,gas:'0x4bfd200'});

    console.log(receipt);
    console.log(result);
}

ContractInstanceTest();

// expected output 
{
  blockHash: '0xc2c4add9a70027aee50c52508860c0e96d611591e1aac6e0d24f506a12189e0e',
  blockNumber: 43737004,
  contractAddress: null,
  from: '0xd2f15aacd423fb5311583f2c5f63183c5c6f3f62',
  gas: '0x4bfd200',
  gasPrice: '0x5d21dba00',
  gasUsed: 44864,
  input: '0xd14e62b8000000000000000000000000000000000000000000000000000000000000000c',
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  nonce: '0x0',
  senderTxHash: '0x9af270e1985c51a3d4d3e5679d60c937bcecc59dc61d7d2bb8206e27aad8b68a',
  signatures: [
    {
      V: '0x7f6',
      R: '0x518d1e9ca79be895bfb394166e2fc86efae8d626194561b44e712885c8799a11',
      S: '0x4590ba16b5af25f4bf14dd31748734b613ab75521939d42f289e899f9e022ba6'
    }
  ],
  status: true,
  to: '0x4ed05eb7ea2e2a0a8569b7d532beb2425b8c63cb',
  transactionHash: '0x9af270e1985c51a3d4d3e5679d60c937bcecc59dc61d7d2bb8206e27aad8b68a',
  transactionIndex: 0,
  type: 'TxTypeSmartContractExecution',
  typeInt: 48,
  value: '0x0',
  events: {}
}
// console.log(result);
43737211
profile
좋은 습관을 가지고 싶은 평범한 개발자입니다.

0개의 댓글