在 NodeJs 中调用我的合约方法时执行恢复

Posted

技术标签:

【中文标题】在 NodeJs 中调用我的合约方法时执行恢复【英文标题】:Execution reverted when calling a method of my contract in NodeJs 【发布时间】:2021-05-31 14:31:14 【问题描述】:

我正在尝试在 Solidity 中创建自己的令牌,并使用 Web3 将令牌从一个帐户转移到另一个帐户,使用 NodeJS/ExpressJS

我一直在使用Infurarinkeby

我可以调用我的方法balanceOf,但我不能调用transferFrom

错误:

返回错误:执行恢复

const express = require('express');
const app = express();
const web3 = require('web3');

const INFURA_BASE_URL = 'https://rinkeby.infura.io/v3/';
const INFURA_API_KEY = '........';
web3js = new web3(new web3.providers.HttpProvider(INFURA_BASE_URL + INFURA_API_KEY));

/*
  Sender & Receiver keys
 */
const SENDER_PUBLIC_KEY = '........';
const SENDER_PRIVATE_KEY = '.......';
const RECEIVER_PUBLIC_KEY = '......';

/*
  Contract ABI.
 */
const CONTRACT_ABI = [
  
    "constant": true,
    "inputs": [],
    "name": "name",
    "outputs": [
      
        "name": "",
        "type": "string"
      
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  ,
  
    "constant": false,
    "inputs": [
      
        "name": "_spender",
        "type": "address"
      ,
      
        "name": "_value",
        "type": "uint256"
      
    ],
    "name": "approve",
    "outputs": [
      
        "name": "success",
        "type": "bool"
      
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  ,
  
    "constant": true,
    "inputs": [],
    "name": "totalSupply",
    "outputs": [
      
        "name": "",
        "type": "uint256"
      
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  ,
  
    "constant": false,
    "inputs": [
      
        "name": "_from",
        "type": "address"
      ,
      
        "name": "_to",
        "type": "address"
      ,
      
        "name": "_value",
        "type": "uint256"
      
    ],
    "name": "transferFrom",
    "outputs": [
      
        "name": "success",
        "type": "bool"
      
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  ,
  
    "constant": true,
    "inputs": [],
    "name": "decimals",
    "outputs": [
      
        "name": "",
        "type": "uint8"
      
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  ,
  
    "constant": false,
    "inputs": [
      
        "name": "_value",
        "type": "uint256"
      
    ],
    "name": "burn",
    "outputs": [
      
        "name": "success",
        "type": "bool"
      
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  ,
  
    "constant": true,
    "inputs": [
      
        "name": "",
        "type": "address"
      
    ],
    "name": "balanceOf",
    "outputs": [
      
        "name": "",
        "type": "uint256"
      
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  ,
  
    "constant": false,
    "inputs": [
      
        "name": "_from",
        "type": "address"
      ,
      
        "name": "_value",
        "type": "uint256"
      
    ],
    "name": "burnFrom",
    "outputs": [
      
        "name": "success",
        "type": "bool"
      
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  ,
  
    "constant": true,
    "inputs": [],
    "name": "symbol",
    "outputs": [
      
        "name": "",
        "type": "string"
      
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  ,
  
    "constant": false,
    "inputs": [
      
        "name": "_to",
        "type": "address"
      ,
      
        "name": "_value",
        "type": "uint256"
      
    ],
    "name": "transfer",
    "outputs": [
      
        "name": "success",
        "type": "bool"
      
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  ,
  
    "constant": false,
    "inputs": [
      
        "name": "_spender",
        "type": "address"
      ,
      
        "name": "_value",
        "type": "uint256"
      ,
      
        "name": "_extraData",
        "type": "bytes"
      
    ],
    "name": "approveAndCall",
    "outputs": [
      
        "name": "success",
        "type": "bool"
      
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function"
  ,
  
    "constant": true,
    "inputs": [
      
        "name": "",
        "type": "address"
      ,
      
        "name": "",
        "type": "address"
      
    ],
    "name": "allowance",
    "outputs": [
      
        "name": "",
        "type": "uint256"
      
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function"
  ,
  
    "inputs": [
      
        "name": "initialSupply",
        "type": "uint256"
      ,
      
        "name": "tokenName",
        "type": "string"
      ,
      
        "name": "tokenSymbol",
        "type": "string"
      
    ],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor"
  ,
  
    "anonymous": false,
    "inputs": [
      
        "indexed": true,
        "name": "from",
        "type": "address"
      ,
      
        "indexed": true,
        "name": "to",
        "type": "address"
      ,
      
        "indexed": false,
        "name": "value",
        "type": "uint256"
      
    ],
    "name": "Transfer",
    "type": "event"
  ,
  
    "anonymous": false,
    "inputs": [
      
        "indexed": true,
        "name": "_owner",
        "type": "address"
      ,
      
        "indexed": true,
        "name": "_spender",
        "type": "address"
      ,
      
        "indexed": false,
        "name": "_value",
        "type": "uint256"
      
    ],
    "name": "Approval",
    "type": "event"
  ,
  
    "anonymous": false,
    "inputs": [
      
        "indexed": true,
        "name": "from",
        "type": "address"
      ,
      
        "indexed": false,
        "name": "value",
        "type": "uint256"
      
    ],
    "name": "Burn",
    "type": "event"
  
];
const CONTRACT_ABI_ADDRESS = '............';

/*
  A controller listening at: http://localhost:3000/send
 */
app.get('/send', async function (req, apiResponse) 

  // Creating contract object
  const contract = new web3js.eth.Contract(CONTRACT_ABI, CONTRACT_ABI_ADDRESS, from: SENDER_PUBLIC_KEY);

  // Check the balance (working good)
  await contract.methods.balanceOf(RECEIVER_PUBLIC_KEY)
    .call()
    .then(res => 
      const str = web3.utils.fromWei(res);
      console.log('balance: ', str);
    )
    .catch(err => 
      console.log(err);
    );

  // Set the allowance (working)
  await contract.methods.approve(SENDER_PUBLIC_KEY, 1)
  .call()
  .then(res => 
     console.log('approve: ', res);
   )
  .catch(err => 
     console.log('Error [approve]', err);
   );

  // Initiate a transfer (not working)
  await contract.methods.transferFrom(SENDER_PUBLIC_KEY, RECEIVER_PUBLIC_KEY, 1)
    .call()
    .then(res => 
      console.log('transferFrom: ', res);
    )
    .catch(err => 
      console.log('Error [transferFrom]', err);
    );

);

app.listen(3000, () => 
  console.log(`Example app listening at http://localhost:3000`)
)

我在 Solidity 中的代码here。

我已经挣扎了好几天没有任何进展。看不出问题出在哪里。

我的目标是在 NodeJS 中将令牌从一个帐户转移到另一个帐户。

【问题讨论】:

【参考方案1】:

因为不满足这个条件,所以在第 106 行抛出异常:_value <= allowance[_from][msg.sender]

您需要在调用transferFrom() 函数之前设置allowance,很可能是通过调用approve() 函数。

或者,如果您想要简单的转账(不使用配额),请改用transfer() 函数。


另外,call() 用于读取纯/视图函数的输出。如需与外部/公共功能交互(发送以太坊交易),请使用.send(from: <senderAddress>)

来自https://web3js.readthedocs.io/en/v1.3.4/web3-eth-contract.html#web3-eth-contract的示例

contract.methods.somFunc().send(from: ....)

【讨论】:

感谢您的回复。我曾尝试在transferFrom 之前致电approve(),但我收到了相同的错误消息。检查我使用approve() 编辑的问题。关于send(),我不确定是否应该在我的 NodeJs 代码中的三个方法中使用它。尝试仅将其与 transferFrom 一起使用,但收到不同的错误。你能再给我支票,再帮我一把吗? 是的,你应该使用send(from: ...)而不是call()...命名有点不幸,但我会尝试简单描述:call()在JS中用于只读。每当您需要将数据写入区块链(使用合约的外部或公共功能)时,您都需要发送以太坊交易 - 这是使用 JS 中的send() 函数完成的。 知道了。我在三个调用中都使用了.send(from: SENDER_PUBLIC_KEY),但是transferFrom() 方法返回The method eth_sendTransaction does not exist/is not available。很奇怪,因为我的合同中没有使用该名称的方法。你知道我该如何解决这个新错误吗?顺便感谢您的帮助。 您走在正确的道路上。 eth_sendTransaction 是您正在调用的节点的 JSON-RPC 方法(在您的情况下是 Infura 的一些节点)。这意味着,流程已经通过了 JS 代码,生成了 Ethereum tx,将 tx 提交给节点,现在节点拒绝了它......这已经超出了我的专业知识,但我猜你有一些不正确的凭据连接到节点(因为 Infura 是广泛使用的提供商,他们不太可能限制这一点)。 显然,Infura 不允许发送未签名的交易。一个话题here。似乎我需要做很多工作,签名,序列化和发送。我不确定我应该在代码的哪一部分执行此操作,我想是在调用 transferFrom() 方法之前?我不明白正确的步骤和首先需要做什么。

以上是关于在 NodeJs 中调用我的合约方法时执行恢复的主要内容,如果未能解决你的问题,请参考以下文章

使用Nodejs部署智能合约

未处理的拒绝(错误):调用恢复异常

Solidity智能合约的重入攻击

调用合约方法并手动签名时出错。 SendTransaction 有效 SendRawTransaction 无效

使用 Web3 1.0 调用智能合约方法

从新创建的帐户中使用 web3 调用合约方法