Kaikas account 연동하여 컨트랙 메서드를 실행하려고 하는데 관련 질문드립니다

안녕하세요,
Klaytn IDE에서 저희 컨트랙을 배포하고 caver.js로 프론트에서 Bapp 로직을 구현하고 있습니다.
저희 프론트에서 사용자 인증을 Kaikas로 진행하고 있는데요,
Kaikas account를 연동하여 컨트랙 메서드를 실행하는 중에 두가지 궁금증이 있어서 문의드립니다.

Kaikas 로그인에 성공하면 window.klay.selectedAddress 값에 현재 로그인한 지갑 주소 값이 저장되어서 사용할 수 있습니다.
그리고 상태를 변경하는 컨트랙트 메서드를 실행할 때, 아래와 같이 send메서드를 호출할때 wallet address를 from의 인자 값으로 전달하려고 합니다.
(사용자가 UI에서 특정 부분을 선택하면 코인 전송이 발생하며 관련된 객체의 상태정보를 변경시키는 메서드)

myContract.methods.buyGrid('0x0eB1f74667eB20B1f7f89872327B7967A804b8a0')
                      .send({
                        from: {wallet_address} // cav.klay.accounts.wallet[0].address로 설정하면 정상 실행 됨.
                        gas: '200000',
                        value: 1_000_000_000_000_000_0 // 0.01 KLAY
                      })
                      .then((receipt) => {
                        console.log(receipt)
                      }).catch((error) => {
                        console.log(error)
                    }))

이때 window.klay.selectedAddress 값을 바로 설정하면

Error: Returned error: unknown account
    at Object.ErrorResponse (errors.js:87)
    at index.js:155
 ...
    at endReadableNT (_stream_readable.js:1010)
    at afterTickTwo (index.js:28)

이렇게 에러가 리턴이 되어서 좀 살펴보니 caver.klay.accounts.wallet에 해당 주소를 추가를 미리 해주어야하는 것 같아서 컨트랙 메서드 실행 전에 아래와 같이 지갑 주소를 caver.klay.accounts.wallet에 추가해주었습니다.

          caver.klay.accounts.wallet.add(
          {
            privateKey: '0x{제 kaikas 프라이빗키}',
            address: klaytn.selectedAddress
          })

이렇게 설정을 해주니 일단 buyGrid 메서드는 실행이 되더군요,

여기서 한가지 의문점은 사용자로부터 kaikas, klip등과 같은 지갑 플러그인을 통해 인증을 받고 이 정보를 바탕으로 bapp 로직의 바탕이 되는 컨트랙 메서드들을 구동하려고 하는데요, 이 privateKey는 꼭 필요한 정보일까요?
caver.js Counter 예제를 좀 살펴보니 여기서는 사용자로부터 privatekey와 password를 직접 입력을 받아서 caver.klay.accounts에 추가를 하는 방식으로 구현을 하더군요.

저희 같은 케이스는 사용자로부터 컨트랙 실행할때마다 privateKey를 입력받기도 곤란하고 어떻게 처리하면 좋을지 막막하군요.

  1. caver.js Documentation을 읽어보니 컨트랙 메서드 실행 후에 on 혹은 once라는 키워드를 통해 각 이벤트에 대한 콜백을 설정할 수가 있는 것 같은데요. 아래와 같이 구현하니 on, once라는 메서드를 찾을 수 없다는 에러가 발생합니다.
Uncaught TypeError: myContract.methods.buyGrid(...).send(...).once is not a function
    at onClick (Footer.jsx:215)
...
    at react-dom.development.js:8508

콘솔 로그로 myContract.methods.buyGrid(…).send(…)에 대한 타입을 살펴보니 단순한 javascript Promise 객체로 리턴이 되는 것 같아서 이렇게 .then으로 처리를 하니 일단 이벤트 처리에는 성공하였습니다.

myContract.methods.buyGrid(...).send(...).then((receipt) => {
                        console.log(receipt)
                      }).catch((error) => {
                        console.log(error)
                    }))

https://docs.kaikas.io/02_api_reference/02_caver_methods

다만 이 매뉴얼에 나온대로 트랜잭션 실행 후에 각 이벤트에 대한 콜백을 상세히 지정할 수 있으면 더 좋을 것 같아서요,
어떻게 하면 on, once등의 메서드를 실행할 수 있을까요?

항상 감사드립니다.

send를 한 뒤에 이벤트 처리는 then 혹은 on을 통해서만 가능합니다.
여기 문서를 보시면 예제가 나와있는데, 예제에서도 then 혹은 on만을 사용하고 있습니다.

이와 별개로 myContract.once는 별도로 단일 이벤트를 subscribe할 수 있는 기능입니다.
이는 함수를 실행시키는 send와는 별개로 따로 subscribe를 구현해 주셔야 합니다.
https://ko.docs.klaytn.com/bapp/sdk/caver-js/api-references/caver.contract#mycontract-once예제에 보면 사용 방법은 나와있습니다.

1 Like

답변 감사합니다.
저희 Bapp에서 선언된 contract 객체에 바로 on을 호출해보니 아래와 같이 에러가 발생합니다.

myContract.send({
                    from: cav.klay.accounts.wallet[0].address,
                    gas: '200000',
                    value: 1_000_000_000_000_000_0 // 0.01 KLAY
                  }, 'buyGrid', '0x0eB1f74667eB20B1f7f89872327B7967A804b8a0')
                      .on('error', function(error) { console.log(error) })
                      .on('transactionHash', function(transactionHash) { console.log(transactionHash) })
                      .on('receipt', function(receipt) {
                        console.log(receipt.contractAddress) // contains the new contract address
                      })

실행 결과
Footer.jsx:206 Uncaught TypeError: myContract.send(...).on is not a function
    at onClick (Footer.jsx:206)
...

console.log로 send 메서드 호출이후 브라우저에서 리턴되는 객체 타입을 확인해보니 아래와 같이 Promise 타입인 것 같구요.

1. Promise {<pending>}

  1. [[Prototype]]: Promise

    1. catch: ƒ catch()
    2. constructor: ƒ Promise()
    3. finally: ƒ finally()
    4. then: ƒ then()
    5. Symbol(Symbol.toStringTag): "Promise"
    6. [[Prototype]]: Object

  2. [[PromiseState]]: "pending"
  3. [[PromiseResult]]: undefined

참조해주신 예제를 살펴 보니 on 메서드가 호출하는 경우가 컨트랙트 메서드에서 deploy를 호출하는 것 같네요
컨트랙 객체 대해서 다시 deploy를 호출하는 경우는 어떤 경우에 해당할까요?
klaytn IDE에서 이미 배포한 컨트랙트에 대해서 메서드 인터페이스가 변경되는 부분이 없는데 또 배포를 하는 이유가 있을까 해서요~

myContract.deploy({ ...}).send({}, function(error, transactionHash) { ... })
    .on('error', function(error) { ... })
    .on('transactionHash', function(transactionHash) { ... })
    .on('receipt', function(receipt) {...})
    .then(function(newContractInstance) {...})

@Jamie
혹시 첫번째 이슈에 대한 답변은 어려우실까요??

deploy 함수는 Contract의 메소드로 구현되어 있습니다.
그렇기 때문에 Contract 인스턴스를 생성한 뒤에 .deploy 메소드를 호출하는 방식입니다.
일반적인 배포 시나리오에서는 아직 배포되지 않은 컨트랙트를 const notDeployedYet = caver.contract.creat(abi) 이런식으로 생성한 뒤 const deployed = notDeployedYet.deploy(...).send(...) 이런식으로 사용되어야 합니다.

첫 번째 질문은 카스카스 튜토리얼 올라온거 참고하시면 좋을 것 같네요.
더 자세한 답변은 다른 분이 답변해 주시는게 더 정확하게 답변드릴 수 있을 것 같네요.