Transaction 관련 질문 드립니다

안녕하세요. DAPP 개발중에 문의 드립니다.

KIP-17 판매 및 구매를 caver.klay.signTransaction 메소드와 caver.klay.sendTransaction 를 사용해서 구현하고 있습니다.

//EXAMPLE
const siginedTransaction = await caver.klay.signTransaction({
      type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION',
      from: sender,
      to: DEPLOYED_ADDRESS,
      data : tsContract.methods.setForSale(tokenId, caver.utils.toPeb(amount, 'KLAY')).encodeABI(),
      gas: '500000',
      value: caver.utils.toPeb('0', 'KLAY'),
    })
    const senderRawTransaction = siginedTransaction.rawTransaction
    caver.klay.sendTransaction({
      senderRawTransaction: senderRawTransaction,
      feePayer: feePayer.address,
    })

Kaikas 생성자를 만들어 caver를 사용하고 있구요.
Klaytn DOC에서 말하길 transaction을 메소드를 사용하면 기본적으로 KALY로 구매 판매가 진행됩니다.

여기서 질문 있습니다. 다른 특정 토큰으로 위의 transaction을 만들고 싶다면 어떻게 해야하나요?

EX) NFT(KIP-17)를 KLAY가 아닌 다른 토큰(ERC-20 or KIP-7)으로 거래를 하고싶을 때…

이 기능 처리는 Smart Contract에서 처리해야 하나요? 아니면 위에 사용했던 klay.sendtransaction이 아닌
kip7 과 kip17의 메소드를 합쳐서 만들어야 될까요?

감사합니다! 도와주세요!

안녕하세요.
일단 setForSale 메소드가 잘못 작성되어있는 것으로 보입니다.
일단 아래와같이 작성되어있을 것으로 생각되는데요.

function setForSale(uint256 tokenId, uint256 klayAmount) public payable {
....
}

이는 아래와같이 수정하셔야 합니다.

function setForSale(uint256 tokenId) public payable {
    ....
}

이때 지불되는 클레이는 transaction을 보낼때 value 값을 사용하시게되면 컨트랙트 내부에서 msg.value 를 이용해 값을 받아오실수 있습니다.

만약 다른 토큰을 사용되는 경우 다음과 같은형태로 구현하시면됩니다.

function setForSale(uint256 tokenId, address tokenAddress, uint256 amount) public {
    IKIP7 tokenForPay = IKIP7(tokenAddress); // IKIP7.sol은 클레이튼 공식 컨트랙트 깃허브 참고하세요.
    require(tokenForPay.balanceOf(msg.sender) >= amount);  // 해당 수량 보유하고있는지 확인
    tokenForPay.approve(address(this), amount); // 본 컨트랙트가 transferFrom을 이용해 amount 만큼 토큰을 뺄수있도록 allowance 설정.
    tokenForPay.transferFrom(msg.sender, address(this), amount);
    mint(tokenId, msg.sender); // mint ABI가 mint(uint256 tokenId, address to) 인 경우
}

답변되셨길 바랍니다.
감사합니다.

1개의 좋아요

답변 너무 감사드립니다.

한가지 더 질문이 있습니다.

다른 토큰을 사용해서 하는 경우 IKIP7.sol을 사용하셨는데 ERC20.sol를 사용해도 무관할까요?

네 사용하셔도 무방하셔요. 다만 커스터마이징 되서 추가된 메소드가 있는경우 해당 토큰컨트랙트의 ABI를 이용하시는것을 추천드립니다. 표준기능만 쓰신다면 IKIP17나 ERC20 사이에 큰 차이는 없을것으로 보여요.

1개의 좋아요

smart contract의 sale 과 purchase 관련 메소드를 다음과 같이 작성하였습니다.

// 판매 등록
  function setForSale(uint256 _tokenId, uint256 _price) public {
      address tokenOwner = nftAddress.ownerOf(_tokenId);
      require(tokenOwner == msg.sender, "caller is not token owner");
      //토큰 소유자만 판매가능
      require(_price > 0 , "price is zero or lower");
      //판매시 가격은 0보다 커야함
      require(nftAddress.isApprovedForAll(tokenOwner, address(this)), "token owner did not approve TokenSales contract");
      //토큰 컨트랙이 대신 승인하도록 승인했는지 확인
      tokenPrice[_tokenId] = _price;
  }
// 구매 

  function purchaseToken(uint256 _tokenId, address _tokenAddress, uint256 _price) public payable {
      ERC20 tokenForPay = ERC20(_tokenAddress);
      uint256 price = tokenPrice[_tokenId];
      address tokenSeller = nftAddress.ownerOf(_tokenId);
      require(tokenForPay.balanceOf(msg.sender) >= price, "caller does not have enough tokens");
      require(_price >= price, "caller sent token lower than price");
      require(msg.sender != tokenSeller, "caller is token seller");
      address payable payableTokenSeller = address(uint160(tokenSeller));
      tokenForPay.transferFrom(msg.sender, payableTokenSeller, price);
      nftAddress.safeTransferFrom(tokenSeller, msg.sender, _tokenId);
      tokenPrice[_tokenId] = 0;
  }

이를 실행하기 위하여

프론트쪽에서

      const { rawTransaction: senderRawTransaction } = await caver.klay.signTransaction({
        type: 'FEE_DELEGATED_SMART_CONTRACT_EXECUTION',
        from: sender,
        to: DEPLOYED_ADDRESS_TOKENSALES,
        data : tsContract.methods.purchaseToken(tokenId, TokenAddress, price).encodeABI(),
        gas: '500000',
      }, signedMessage)

      caver.klay.sendTransaction({
        senderRawTransaction: senderRawTransaction,
        feePayer: feePayer.address,
      })

이런 방식으로 요청하려고 합니다.

하지만 error를 반환합니다.

Uncaught (in promise) Error: evm: execution reverted
 {
  "blockHash": "0xd9a7a45121a73c0e365b4e712d0c43069d731cb5084b85e27e4deabb8c46ad07",
  "blockNumber": 85675225,
  "contractAddress": null,
  "feePayer": "0xda22d2127a3a9b6f218fd975f2ee4312726626e7",
  "feePayerSignatures": [
    {
      "V": "0x7f5",
      "R": "0x5ff2da40d894f04f1cfb87f5416ce76dfc8295823d03c3165cc15346be55eaf4",
      "S": "0x19c532dea9dc491b50b86704a6b57a7af11e68d968ccae9770042ca708d3aa4d"
    }
  ],
  "from": "0x6915b0828a99072d885aeaeb549d45978bdba02a",
  "gas": "0x7a120",
  "gasPrice": "0xae9f7bcc00",
  "gasUsed": 88758,
  "input": "0x9b60cc97000000000000000000000000000000000000000000000000000000000000000100000000000000000000000014078e02e3ea9cb650826431a08856c607a72ab50000000000000000000000000000000003c2f7086aed236c807a1b5000000000",
  "logs": [],
  "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "nonce": "0x21",
  "senderTxHash": "0x37bb49f6a9b8a815282c6cbb45fde440af52436b418c341e21d4279f01aac37e",
  "signatures": [
    {
      "V": "0x7f5",
      "R": "0xffbcfd22cfc949034f18b27f47f7c8d643d3880c3cba99d11c00784f8efb7704",
      "S": "0x445d7496acc3ea37fef22ea8fb760b3dfa1b3b346bbfa895eb01573e13d3a72"
    }
  ],
  "status": false,
  "to": "0xdedbb16b00d62a9ea71cb1e62ed461c3371e9810",
  "transactionHash": "0xe32d3d0a0e37f15a8c3027e632c90118f37b009b7dc4c427c8f7eb032a02bcf2",
  "transactionIndex": 1,
  "txError": "0x9",
  "type": "TxTypeFeeDelegatedSmartContractExecution",
  "typeInt": 49,
  "value": "0x0"
}
    at checkForNormalTx (index.js:765)
    at eval (index.js:642)
checkForNormalTx @ index.js:765
eval @ index.js:642
Promise.then (async)
buyToken @ index.js:516
async function (async)
buyToken @ index.js:496
onclick @ (index):1

프론트쪽에서 smart Contract를 실행시키려면 어떤 caver js 메소드 중 klay.sendTransaction()을 사용하고 있는데 다른걸 사용해야 될까요? 아니면 내부적 데이터 값들을 변경해야 할까요?

잘못된 부분 지적해주시면 감사하겠습니다!!

저는 개인적으로 컴파일해서 생성된 json파일에서 ‘abi’ 부분의 데이터를 가져와서 high-level 메소드 이용해서 호출합니다. 예를들면

const contractABI = require('XXX.json')['abi']
const myContract = new caver.contract(contractABI, contractAddress)

const receipt = await myContract.methods.purchaseToken(tokenId, tokenAddress, price).send({from: address who sign, gas: 500000})

console.log(receipt)

또는 현재 컨트랙트 알려주신것 확인해보면 에러나는 부분은, payable 이 선언되어있어서 그렇습니다.
payable은 오로지 klay를 지급받아서 msg.value 값을 컨트랙트 내부에서 받아올때 사용하신다고 생각하시면됩니다.
제 생각으로는 payable 을 제거하시면 아마 문제없이 실행될것으로 보입니다.

감사합니다.

2개의 좋아요

친절한 설명 너무 감사드립니다 !! :grinning: