简体   繁体   English

以 Truffle 教程为基础 - 添加返回宠物 Function - RPC 错误

[英]Building on Truffle Tutorial - Adding Return Pet Function - RPC Error

I'm building on top of the tutorial provided by Truffle here: https://trufflesuite.com/tutorial/index.html我建立在 Truffle 提供的教程之上: https://trufflesuite.com/tutorial/index.html

I'm trying to add a returnPet function similar to: Adding a return function to Truffles Pet Shop我正在尝试添加 returnPet function 类似于: Adding a return function to Truffles Pet Shop

However, I'm running into an RPC error when using metamask when I click on the 'Return' button on a pet.但是,当我单击宠物上的“返回”按钮时,我在使用元掩码时遇到了 RPC 错误。

MetaMask - RPC Error: [ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"data":{"message":"VM Exception while processing transaction: revert","code":-32000,"data":{"0x36735e441b9c6ffeb61bcebce098576993c38580ecc207f99043706a074a06e4":{"error":"revert","program_counter":70,"return":"0x"},"stack":"RuntimeError: VM Exception while processing transaction: revert\n    at Function.RuntimeError.fromResults (/tmp/.mount_ganachz4CZxO/resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\n    at BlockchainDouble.processBlock (/tmp/.mount_ganachz4CZxO/resources/static/node/node_modules/ganache-core/lib/blockchain_double.js:627:24)\n    at runMicrotasks (<anonymous>)\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)","name":"RuntimeError"}}}}' 
Object { code: -32603, message: "[ethjs-query] while formatting outputs from RPC '{\"value\":{\"code\":-32603,\"data\":{\"message\":\"VM Exception while processing transaction: revert\",\"code\":-32000,\"data\":{\"0x36735e441b9c6ffeb61bcebce098576993c38580ecc207f99043706a074a06e4\":{\"error\":\"revert\",\"program_counter\":70,\"return\":\"0x\"},\"stack\":\"RuntimeError: VM Exception while processing transaction: revert\\n    at Function.RuntimeError.fromResults (/tmp/.mount_ganachz4CZxO/resources/static/node/node_modules/ganache-core/lib/utils/runtimeerror.js:94:13)\\n    at BlockchainDouble.processBlock (/tmp/.mount_ganachz4CZxO/resources/static/node/node_modules/ganache-core/lib/blockchain_double.js:627:24)\\n    at runMicrotasks (<anonymous>)\\n    at processTicksAndRejections (internal/process/task_queues.js:93:5)\",\"name\":\"RuntimeError\"}}}}'" }

I'm maintaining this work here: https://github.com/mpoletiek/pet-shop-tutorial我在这里维护这项工作: https://github.com/mpoletiek/pet-shop-tutorial

I'm not sure where to begin.我不知道从哪里开始。 Before approving the transaction in Metamask, it warns:在 Metamask 中批准交易之前,它会发出警告:

This transaction is expected to fail. Trying to execute it is expected to be expensive but fail, and is not recommended.

I click 'I will try anyway' to generate the RPC error.我单击“无论如何我都会尝试”以生成 RPC 错误。

Here is my Solidity contract:这是我的 Solidity 合同:

pragma solidity ^0.5.0;

contract Adoption {
    address[16] public adopters;

    // Adopting a pet
    function adopt(uint petId) public returns (uint) {
        require(petId >= 0 && petId <= 15);

        adopters[petId] = msg.sender;

        return petId;
    }

    // Retrieving the adopters
    function getAdopters() public view returns (address[16] memory) {
        return adopters;
    }
    
    // Return Pet
    function returnPet(uint petId) public returns (address) {
        require(petId >= 0 && petId <= 15);
        
        // Address must own the pet
        require(msg.sender == adopters[petId]);
        
        // Clear the adopter for this pet
        adopters[petId] = address(0);
        
        return adopters[petId];
    }

}

and app.js:和 app.js:

App = {
  web3Provider: null,
  accounts: [],
  contracts: {},
  adoptionInstance: null,

  init: async function() {
    // Load pets.
    $.getJSON('../pets.json', function(data) {
      var petsRow = $('#petsRow');
      var petTemplate = $('#petTemplate');

      for (i = 0; i < data.length; i ++) {
        petTemplate.find('.panel-title').text(data[i].name);
        petTemplate.find('img').attr('src', data[i].picture);
        petTemplate.find('.pet-breed').text(data[i].breed);
        petTemplate.find('.pet-age').text(data[i].age);
        petTemplate.find('.pet-location').text(data[i].location);
        petTemplate.find('.btn-adopt').attr('data-id', data[i].id);
        petTemplate.find('.btn-return').attr('data-id', data[i].id);

        petsRow.append(petTemplate.html());
      }
    });

    return await App.initWeb3();
  },

  initWeb3: async function() {

    // Modern dapp browsers...
    if (window.ethereum){
      try {
        //Request account access
        App.accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
      } catch (error) {
        // User denied account access...
        console.error("User denied account access");
      }
      
      // User granted access to accounts
      console.log("Account[0]: "+App.accounts[0]);
      
      App.web3Provider = window.ethereum;
      console.log("modern dapp browser");
    }
    // Legacy dapp browsers...
    else if (window.web3) {
      App.web3Provider = window.web3.currentProvider;
      App.accounts = window.eth.accounts;
      console.log("legacy dapp browser");
    }
    // if no injected web3 instance is detected, fall back to Ganache
    else {
      App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
    }
    
    web3 = new Web3(App.web3Provider);

    return App.initContract();
    
  },

  initContract: function() {

    $.getJSON('Adoption.json', function(data) {
      // Get the necessary contract artifact file and instantiate it with @truffle/contract
      var AdoptionArtifact = data;
      try { App.contracts.Adoption = TruffleContract(AdoptionArtifact); } catch (error) { console.error(error); }

      // Set the provider for our contract
      try {
        App.contracts.Adoption.setProvider(App.web3Provider);
      } catch (error){
        console.log(error);
      }
      // Use our contract to retrieve and mark the adopted pets
      return App.markAdopted();
    });

    return App.bindEvents();
  },

  bindEvents: function() {
    $(document).on('click', '.btn-adopt', App.handleAdopt);
    $(document).on('click', '.btn-return', App.handleReturn);
  },

  markAdopted: function() {

    var adoptionInstance;

    App.contracts.Adoption.deployed().then(function(instance) {
      App.adoptionInstance = instance;

      return App.adoptionInstance.getAdopters.call();
    }).then(function(adopters) {
        
        for(i=0;i<adopters.length;i++){
            if (adopters[i] != '0x0000000000000000000000000000000000000000') {
                if (adopters[i] == App.accounts[0]){
                    $('.panel-pet').eq(i).find('.btn-return').text('Return').attr('disabled', false);
                    $('.panel-pet').eq(i).find('.btn-adopt').text('Adopted').attr('disabled', true);
                } else {
                    $('.panel-pet').eq(i).find('.btn-return').text('-').attr('disabled', true);
                    $('.panel-pet').eq(i).find('.btn-adopt').text('Adopted').attr('disabled', true);
                }
            } else {
                $('.panel-pet').eq(i).find('.btn-return').text('-').attr('disabled', true);
            }
        }
        
        
        
    }).catch(function(err) {
      console.log(err.message);
    });
  },

  handleReturn: function(event) {
    event.preventDefault();
    
    var petId = parseInt($(event.target).data('id'));
    
    console.log("petID:"+petId);
        
    App.contracts.Adoption.deployed().then(function(instance) {

        // Execute adopt as a transaction by sending account
        return App.adoptionInstance.returnPet(petId, {from: App.accounts[0]});
      }).then(function(result) {
        return App.markAdopted();
      }).catch(function(err) {
        console.log(err.message);
      });
    
  },


  handleAdopt: function(event) {
    event.preventDefault();

    var petId = parseInt($(event.target).data('id'));
    console.log("petId:"+petId);

    App.contracts.Adoption.deployed().then(function(instance) {

        // Execute adopt as a transaction by sending account
        return App.adoptionInstance.adopt(petId, {from: account[0]});
        }).then(function(result) {
            return App.markAdopted();
        }).catch(function(err) {
            console.log(err.message);
    });
    
  }

};

$(function() {
  $(window).load(function() {
    App.init();
  });
});

Everything compiles and my tests run okay.一切都编译了,我的测试运行正常。 Not sure how Metamask knows the transaction is bad before I confirm, but is that a hint?在我确认之前不确定 Metamask 如何知道交易是坏的,但这是一个提示吗? I get the same result in Firefox and Chrome if that helps.如果有帮助,我在 Firefox 和 Chrome 中得到相同的结果。

Any ideas what I should check first?任何想法我应该首先检查什么?

Thanks,谢谢,

Based on the provided code, you're trying to return a pet that hasn't been yet adopted, so it fails on this logical error.根据提供的代码,您正在尝试返回尚未被领养的宠物,因此在此逻辑错误上失败。

First you deploy the contract.首先你部署合约。 Since there's no adoption mechanism in the constructor, there are no adopted pets after this step.由于构造函数中没有收养机制,所以这一步之后就没有收养的宠物了。

App.contracts.Adoption.deployed()

And immediately after that you're trying to return a pet紧接着你就试图归还一只宠物

return adoptionInstance.returnPet

Which fails on this line, because all adopters have the default value 0x0 at this point.在这条线上失败了,因为此时所有采用者的默认值都是 0x0。

require(msg.sender == adopters[petId]);

Solution : Based on the context of your JS code (the comment and return App.markAdopted() ), you probably wanted to invoke the adopt() function in the JS test - and not the returnPet() .解决方案:根据您的 JS 代码的上下文(注释和return App.markAdopted() ),您可能想在 JS 测试中调用adopt() function 而不是returnPet()

So I finally got this working after resetting my entire environment and rebuilding with truffle migrate --reset .因此,在重置整个环境并使用truffle migrate --reset进行重建后,我终于完成了这项工作。 I also had to ensure I only had 1 browser tab open to the app, multiple tabs resulted in multiple transactions for some reason I'm still unaware of.我还必须确保我只有 1 个浏览器选项卡对应用程序打开,多个选项卡会导致多个交易,原因我仍然不知道。

app.js应用程序.js

App = {
  web3Provider: null,
  accounts: [],
  contracts: {},

  init: async function() {
    // Load pets.
    $.getJSON('../pets.json', function(data) {
      var petsRow = $('#petsRow');
      var petTemplate = $('#petTemplate');

      for (i = 0; i < data.length; i ++) {
        petTemplate.find('.panel-title').text(data[i].name);
        petTemplate.find('img').attr('src', data[i].picture);
        petTemplate.find('.pet-breed').text(data[i].breed);
        petTemplate.find('.pet-age').text(data[i].age);
        petTemplate.find('.pet-location').text(data[i].location);
        petTemplate.find('.btn-adopt').attr('data-id', data[i].id);
        petTemplate.find('.btn-return').attr('data-id', data[i].id);

        petsRow.append(petTemplate.html());
      }
    });

    return await App.initWeb3();
  },

  initWeb3: async function() {

    // Modern dapp browsers...
    if (window.ethereum){
      try {
        //Request account access
        App.accounts = await window.ethereum.request({ method: "eth_requestAccounts" });
      } catch (error) {
        // User denied account access...
        console.error("User denied account access");
      }
      
      // User granted access to accounts
      console.log("Account[0]: "+App.accounts[0]);
      
      App.web3Provider = window.ethereum;
      console.log("modern dapp browser");
    }
    // Legacy dapp browsers...
    else if (window.web3) {
      App.web3Provider = window.web3.currentProvider;
      App.accounts = window.eth.accounts;
      console.log("legacy dapp browser");
    }
    // if no injected web3 instance is detected, fall back to Ganache
    else {
      App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
    }
    
    //web3 = new Web3(App.web3Provider);

    return App.initContract();
    
  },

  initContract: function() {

    $.getJSON('Adoption.json', function(data) {
      // Get the necessary contract artifact file and instantiate it with @truffle/contract
      var AdoptionArtifact = data;
      try { App.contracts.Adoption = TruffleContract(AdoptionArtifact); } catch (error) { console.error(error); }

      // Set the provider for our contract
      try {
        App.contracts.Adoption.setProvider(App.web3Provider);
      } catch (error){
        console.log(error);
      }
      // Use our contract to retrieve and mark the adopted pets
      return App.markAdopted();
    });

    return App.bindEvents();
  },

  bindEvents: function() {
    $(document).on('click', '.btn-adopt', App.handleAdopt);
    $(document).on('click', '.btn-return', App.handleReturn);
  },

  markAdopted: function() {

    var adoptionInstance;
    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;

      return adoptionInstance.getAdopters.call();
    }).then(function(adopters) {
        
        for(i=0;i<adopters.length;i++){
            if (adopters[i] != '0x0000000000000000000000000000000000000000') {
                if (adopters[i] == App.accounts[0]){
                    $('.panel-pet').eq(i).find('.btn-return').text('Return').attr('disabled', false);
                    $('.panel-pet').eq(i).find('.btn-adopt').text('Adopted').attr('disabled', true);
                } else {
                    $('.panel-pet').eq(i).find('.btn-return').text('-').attr('disabled', true);
                    $('.panel-pet').eq(i).find('.btn-adopt').text('Adopted').attr('disabled', true);
                }
            } else {
                $('.panel-pet').eq(i).find('.btn-return').text('-').attr('disabled', true);
            }
        }

        
    }).catch(function(err) {
      console.log(err.message);
    });
  },

  handleReturn: function(event) {
    event.preventDefault();
    
    var petId = parseInt($(event.target).data('id'));
    
    console.log("handleReturn petId: "+petId+" Account[0]: "+App.accounts[0]);
    console.log("petID:"+petId);
    console.log("Account[0]: "+App.accounts[0]);
    
    var adoptionInstance;
    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;

      return adoptionInstance.returnPet(petId, {from: App.accounts[0]});
    }).then(function(result){
        App.markAdopted();
    });
    
  },


  handleAdopt: function(event) {
    event.preventDefault();

    var petId = parseInt($(event.target).data('id'));
    console.log("handleAdopt petId: "+petId+" Account[0]: "+App.accounts[0]);
    console.log("petId:"+petId);

    var adoptionInstance;
    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;

      return adoptionInstance.adopt(petId, {from: App.accounts[0]});
    }).then(function(result){
        App.markAdopted();
    });
    
  }

};

$(function() {
  $(window).load(function() {
    App.init();
  });
});

Adoption.sol采用.sol

pragma solidity ^0.5.0;

contract Adoption {
    address[16] public adopters;

    // Adopting a pet
    function adopt(uint petId) public returns (uint) {
        require(petId >= 0 && petId <= 15);

        adopters[petId] = msg.sender;

        return petId;
    }

    // Retrieving the adopters
    function getAdopters() public view returns (address[16] memory) {
        return adopters;
    }
    
    // Return Pet
    function returnPet(uint petId) public returns (uint) {
        require(petId >= 0 && petId <= 15);
        
        // Address must own the pet
        require(msg.sender == adopters[petId]);
        
        // Clear the adopter for this pet
        adopters[petId] = address(0);
        
        return petId;
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM