如何解决Node.js API调用Solidity合约GET接口的AbiError与Gas错误?
使用Web3.js v4.7.0结合Node.js/Express与Solidity智能合约交互,合约在REMIX IDE中运行正常,POST创建交易接口可正常工作,但调用GET接口获取所有交易时,出现AbiError: Parameter decoding error或Out of Gas错误。已尝试调高gasLimit、修改ABI及合约返回类型,问题仍未解决。
合约代码
pragma solidity ^0.8.10; contract Transactions { uint public count = 0; struct Transaction { uint id; string name; uint amount; } mapping(uint => Transaction) public transactions; constructor() { createTransaction("derived", 2000); } function createTransaction(string memory _name, uint _amount) public { count++; transactions[count] = Transaction(count, _name, _amount); } function getAllTransactions() public view returns (Transaction[] memory) { Transaction[] memory allTransactions = new Transaction[](count); for (uint256 index = 1; index <= count; index++) { Transaction storage transaction = transactions[index]; allTransactions[index - 1] = transaction; } return allTransactions; } }
API路由代码
function routes(app, db, accounts, transactionList) { app.get("/transactions", async (req, res) => { try { const gasLimit = 20000000; const allTransactions = await transactionList.methods .getAllTransactions() .call({ gas: gasLimit }); res.json({ allTransactions }); } catch (error) { console.error("Error getting all transactions:", error); res.status(500).json({ error: "Error getting all transactions" }); } }); app.post("/transactions", async (req, res) => { try { const { name, amount } = req.body; await transactionList.methods .createTransaction(name, amount) .send({ from: accounts[0] }); const transaction = { name: name, amount: amount, }; const result = await db.collection("transactions").insertOne(transaction); res.status(201).json({ message: "Transaction created Successfully", contactId: result.insertedId, }); } catch (e) { console.log("Error creating transaction:", e); res.status(500).json({ error: "Internal server error" }); } }); } module.exports = routes;
错误信息
Error getting all transactions: AbiError: Parameter decoding error: Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.
at decodeParametersWith (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-abi/lib/commonjs/api/parameters_api.js:103:15)
at decodeParameters (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-abi/lib/commonjs/api/parameters_api.js:213:75)
at decodeMethodReturn (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-contract/lib/commonjs/encoding.js:125:56)
at ContractBuilder. (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-contract/lib/commonjs/contract.js:672:61)
at Generator.next ()
at fulfilled (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-contract/lib/commonjs/contract.js:21:58)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
innerError: undefined,
code: 205,
props: {
internalErr: AbiError: Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.
at decodeParametersWith (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-abi/lib/commonjs/api/parameters_api.js:94:19)
at decodeParameters (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-abi/lib/commonjs/api/parameters_api.js:213:75)
at decodeMethodReturn (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-contract/lib/commonjs/encoding.js:125:56)
at ContractBuilder. (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-contract/lib/commonjs/contract.js:672:61)
at Generator.next ()
at fulfilled (/home/steve/Projects/vcis_wallet_blockchain/node_modules/web3/node_modules/web3-eth-contract/lib/commonjs/contract.js:21:58)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
innerError: undefined,
code: 205,
props: {}
}
}
}
ABI代码
const TRANSACTION_ADDRESS = "0x0766512529e545515d908c2cf8962455c430D3d7"; const TRANSACTION_ABI = [ { inputs: [ { internalType: "string", name: "_name", type: "string", }, { internalType: "uint256", name: "_amount", type: "uint256", }, ], name: "createTransaction", outputs: [], stateMutability: "nonpayable", type: "function", }, { inputs: [], stateMutability: "nonpayable", type: "constructor", }, { inputs: [], name: "count", outputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], stateMutability: "view", type: "function", }, { inputs: [], name: "getAllTransactions", outputs: [ { components: [ { internalType: "uint256", name: "id", type: "uint256", }, { internalType: "string", name: "name", type: "string", }, { internalType: "uint256", name: "amount", type: "uint256", }, ], internalType: "struct Transactions.Transaction[]", name: "", type: "tuple[]", }, ], stateMutability: "view", type: "function", }, { inputs: [ { internalType: "uint256", name: "", type: "uint256", }, ], name: "transactions", outputs: [ { internalType: "uint256", name: "id", type: "uint256", }, { internalType: "string", name: "name", type: "string", }, { internalType: "uint256", name: "amount", type: "uint256", }, ], stateMutability: "view", type: "function", }, ]; module.exports = { TRANSACTION_ABI, TRANSACTION_ADDRESS, };
入口文件index.js
const express = require("express"); const cors = require("cors"); const { MongoClient } = require("mongodb"); const { Web3 } = require("web3"); const routes = require("./router/routes"); const { TRANSACTION_ABI, TRANSACTION_ADDRESS } = require("./config"); const app = express(); app.use(cors()); app.use(express.json()); // Define MongoDB connection URI const uri = ""; // Define the async function to connect to MongoDB and perform operations async function run() { let client; try { client = new MongoClient(uri); await client.connect(); const db = client.db("Cluster0"); const web3 = new Web3( new Web3.providers.HttpProvider("http://127.0.0.1:7545") ); const accounts = await web3.eth.getAccounts(); const transactionList = new web3.eth.Contract( TRANSACTION_ABI, TRANSACTION_ADDRESS ); routes(app, db, accounts, transactionList); app.listen(process.env.PORT || 3001, () => { console.log("Listening on port " + (process.env.PORT || 3001)); }); console.log("Connected to MongoDB and started server successfully!"); } catch (error) { console.error("Error connecting to MongoDB:", error); } } run().catch(console.error);
解决方案
1. 校验合约部署一致性
确保Node.js代码中使用的合约地址和ABI,与REMIX中实际部署的完全一致。若在REMIX中重新部署合约,必须同步更新TRANSACTION_ADDRESS和TRANSACTION_ABI。
2. 简化view函数调用
view类型函数无需手动指定gasLimit,Web3.js会自动计算所需gas,移除call中的gas参数:
const allTransactions = await transactionList.methods.getAllTransactions().call();
3. 检查本地节点状态
确认Ganache节点(http://127.0.0.1:7545)正常运行,未因重启导致合约地址失效。若节点重启,需重新部署合约并更新地址。
4. 重新导出ABI
从REMIX中直接导出最新ABI,替换现有代码中的TRANSACTION_ABI,避免手动修改导致的格式不匹配。
5. 优化合约边界逻辑
为合约getAllTransactions添加count为0的边界检查,避免空数组解码异常:
function getAllTransactions() public view returns (Transaction[] memory) { if(count == 0) return new Transaction[](0); Transaction[] memory allTransactions = new Transaction[](count); for (uint256 index = 1; index <= count; index++) { allTransactions[index - 1] = transactions[index]; } return allTransactions; }
内容的提问来源于stack exchange,提问作者Steven

