Returning Array instead of an Object when calling automatic getter for a mapping to struct? #1789
-
IntroHi! I'm new here and to ethers.js too. I've being writing some basic contracts and I've observed the behavior described later on. I was wondering if @ricmoo or anyone else could explain (or redirect haven't found anything out there), why would we prefer an Array with both named an unnamed parameters instead of an Object when a struct is returned form a contracts function. Description
CodeSmart contract// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "hardhat/console.sol";
contract Badge {
address public owner = msg.sender;
uint16 currentId = 0;
struct BadgeInfo{
uint16 id;
address issuer;
string issuerName;
address recipient;
string recipientName;
}
mapping(uint16 => BadgeInfo) public badgesById;
event BadgeIssued(uint16 id, string issuerName, string recipientName);
function issue(string memory _issuerName, address _recipient, string memory _recipientName) public {
badgesById[currentId] = BadgeInfo({
id: currentId,
issuer: msg.sender,
issuerName: _issuerName,
recipient: _recipient,
recipientName: _recipientName
});
emit BadgeIssued(badgesById[currentId].id, badgesById[currentId].issuerName, badgesById[currentId].recipientName);
currentId++;
}
} Testconst { expect } = require("chai");
describe("Badge contract", function () {
let owner;
let issuer;
let recipient;
let badgeInfo;
let Badge;
let badge;
before( async function () {
// Save account addresses once to use in each test
const accounts = await ethers.getSigners();
owner = accounts[0];
issuer = accounts[1];
recipient = accounts[2];
// Badge info to use
badgeInfo = {
id: 0,
issuer: issuer.address,
issuerName: "Ivan Illich",
recipient: recipient.address,
recipientName: "Gustavo Esteva"
};
});
beforeEach( async function () {
// Deploy fresh contract before each test
Badge = await ethers.getContractFactory("Badge");
badge = await Badge.deploy();
await badge.deployed();
});
// (...)
it("Should return a badge", async function () {
await badge.issue(badgeInfo.issuerName, badgeInfo.recipient, badgeInfo.recipientName);
const badgeResult = await badge.badgesById(badgeInfo.id);
console.log(badgeResult);
expect(badgeResult).to.deep.equal(badgeInfo);
});
});
}); Output
Steps to reproduce1.- Clone the repo & switch to smart-contracts branch git clone https://github.com/luishporras/web3-server
git checkout smart-contracts 2.- Install deps and run tests npm install
npx hardhat test GratitudeThanks in advance and excuse me if this is not the proper channel. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
This is the perfect channel to discuss this. :) This way the method returns the same type of object whether you use Otherwise code which accepts a contract as a parameter would have to inspect what type of ABI was used for a given method to determine how to decouple it (since it could be an object in some case, and So it makes code that programmatically extends a Contract and Result much easier, since it can always just use positional arguments. And developers can also interact directly with any named parameters. It also gracefully handles the case where you have Basically, ABI encoded data guarantees everything is accessible positionally. But names are just nice-to-have. So arrays are perfect; the guarantee positional access and allow keywords optionally. For your test case you could either use a custom matcher (I might write one at some point for some of these objects) or maybe compare the Array, converting badgeInfo using something like I’m considering adding some extra functionality to the Result object in v6, like an Does that make sense? |
Beta Was this translation helpful? Give feedback.
This is the perfect channel to discuss this. :)
This way the method returns the same type of object whether you use
function bar() returns (uint256, bool)
andfunction bar() returns (uint256 a, bool b)
. The same goes for nesting structures, arrays and anything else.Otherwise code which accepts a contract as a parameter would have to inspect what type of ABI was used for a given method to determine how to decouple it (since it could be an object in some case, and
Array.isArray
andtypeof
Would be of little use, since the result could legitimately be an array).So it makes code that programmatically extends a Contract and Result much easier, since it can always just use positional argument…