How to access Solidity mapping which has value of array type? - solidity

I define a state variable of mapping type, e.g. mapping(uint256 => uint256[]). I thought to make it public so that I can access it from outside of the contract. However, the compiler reports error TypeError: Wrong argument count for function call: 1 arguments given but expected 2.. It looks like the automatic getter of the mapping doesn't return an array.
For example, ContractB is the contract to be built,
pragma solidity >=0.5.0 <0.6.0;
contract ContractB {
mapping(uint256 => uint256[]) public data;
function getData(uint256 index) public view returns(uint256[] memory) {
return data[index];
}
function add(uint256 index, uint256 value) public {
data[index].push(value);
}
}
Creating a test contract to test ContractB,
import "remix_tests.sol"; // this import is automatically injected by Remix.
import "./ContractB.sol";
contract TestContractB {
function testGetData () public {
ContractB c = new ContractB();
c.add(0, 1);
c.add(0, 2);
Assert.equal(c.data(0).length, 2, "should have 2 elements"); // There is error in this line
}
}
I could create a function in ContractB which returns array, though.

Unfortunately, Solidity can't return dynamic arrays yet.
But you can get elements one by one. For this, you need to pass an index to getter:
contract TestContractB {
function testGetData () public {
ContractB c = new ContractB();
c.add(0, 1);
c.add(0, 2);
// Assert.equal(c.data(0).length, 2, "should have 2 elements"); // Don't use this
Assert.equal(c.data(0,0), 1, "First element should be 1");
Assert.equal(c.data(0,1), 2, "Second element should be 2");
}
}

Related

solidity array mapping with array inside struct

Unable to execture createSchema function. It gives following error
revert
The transaction has been reverted to the initial state.
Note: The called function should be payable if you send value and the value you send should be less than your current balance.
Debug the transaction to get more information.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Testing {
struct Schema {
mapping(string => string) entity;
}
struct SchemaMapping {
// mapping(string => string) key;
// mapping(string => string) value;
string[] key;
string[] value;
}
mapping(uint256 => Schema) schemas;
mapping(uint256 => SchemaMapping[]) schemaMappings;
function createSchema(uint256 id, string memory key, string memory value) public {
SchemaMapping[] storage schemamapping = schemaMappings[id];
schemamapping[id].key.push(key);
schemamapping[id].value.push(value);
schemas[id].entity[key] = value;
}
function getSchemaElemet(uint256 id) public view returns (SchemaMapping[] memory) {
return schemaMappings[id];
}
}
I adjusted your smart contract. The issue in your original contract is that you were trying to add values into schemaMapping without create SchemaMapping at specific index.
Smart contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "#openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Testing {
struct Schema {
mapping(string => string) entity;
}
struct SchemaMapping {
string[] key;
string[] value;
}
mapping(uint256 => Schema) schemas;
mapping(uint256 => SchemaMapping[]) schemaMappings;
function createSchema(uint256 id, string memory key, string memory value) public {
SchemaMapping[] storage schemamapping = schemaMappings[id];
// NOTE: I created an empty space in storage for create object and after bind it with values.
SchemaMapping storage singleSchemaItem = schemamapping.push();
// NOTE: I put values inside keys
singleSchemaItem.key.push(key);
singleSchemaItem.value.push(value);
schemas[id].entity[key] = value;
}
function getSchemaElemet(uint256 id) public view returns (SchemaMapping[] memory) {
return schemaMappings[id];
}
}

Get array from struct

so I have this contract
pragma solidity >=0.7.0 <0.9.0;
struct Universe{
uint256 year;
uint64[3] space;
}
contract myContract {
mapping(address => Universe) public myUni;
function setSomething(uint256 _year, uint64[3] memory _space) public {
Universe memory testUni = Universe({
year: _year,
space: _space
});
myUni[msg.sender] = testUni;
}
}
And I am currently trying to test if the array is being passed correctly with truffle:
pragma solidity >=0.7.0 <0.9.0;
const myContract = artifacts.require("./1_Storage.sol");
contract('myContract', (accounts) => {
it('checks if setSomething works', async () => {
const myContractInstance = await myContract.new();
const spaceData = [6000, 6000, 6000];
await myContractInstance.setSomething(2543,spaceData,{from: accounts[1]});
const myPassedData = (await myContractInstance.myUni(accounts[1]));
console.log(myPassedData);
console.log(spaceData);
});
});
The issue is, that I am not able to get spaceData passed into the function and I don't know why. The console.log(myPassedData); shows me only the year and when I try something like console.log(myPassedData.spaceData); it says undefined.
Thanks #sms for the link. As stated in the solidity Documentation:
If you have a public state variable of array type, then you can only
retrieve single elements of the array via the generated getter
function. This mechanism exists to avoid high gas costs when returning
an entire array.
So I had to write a Getter-Function inside of the Contract to be able to access the array:
function getUniverseArray(address _address) public view returns (uint64[3] memory) {
return myUni[_address].space;
}

Iteract with arrays from another contract

Does anyone know how can I read meanings of variables in the array from another contract ? For example:
pragma solidity 0.8.7;
contract Contract1{
uint[] newData ;
constructor(uint _i){
newData.push(_i);
}
}
interface IContract1 {
function newData() external returns(uint[] memory);
}
contract Contract2 {
uint public newOne;
function foo(address _addr, uint _i) external{
newOne = IContract1(_addr).newData()[_i];
}
}
This code has compiled success in the Remix, but I have an error when I trying call foo()
Contract1.newData is an internal (default visibility when you don't specify it) property.
But the interface IContract1 defines an external function named newData() (without the index argument).
You can modify the newData property to have a public visibility, for which the compiler autogenerates a getter function (with the index argument).
Then you also need to update the function definition in the interface, so that it contains the index argument and returns just one item (and not the whole array).
pragma solidity ^0.8;
contract Contract1 {
// changed visibility to `public`
uint[] public newData;
constructor(uint _i){
newData.push(_i);
}
}
interface IContract1 {
// added `_index` argument
// changed the return value to one item of the array
function newData(uint256 _index) external returns(uint);
}
contract Contract2 {
uint public newOne;
function foo(address _addr, uint _i) external{
// changed the call to the getter function
// instead of trying to access the property directly
newOne = IContract1(_addr).newData(_i);
}
}
Note that the Contract1 constructor pushes the first item to the array, so you need to pass 0 (as for the first index) to the foo() function argument uint _i.
Repro steps:
Deploy Contract1 passing it 123 as constructor param. Deployed to address 0x456.
Deploy Contract2
Execute Contract2 function foo(0x456, 0), which will effectively set Contract2.newOne value to 123.

Why does uint256 variable accept array type input?

I have some question between uint256 type and the array.
There is my Solidity sample Code:
contract Test{
constructor(){ }
function getValue(uint256 _value) public returns(uint256) {
return _value;
}
}
I notice that the getValue function can accept array type input.
In order to figure out this problem, I write an unit test case by truffle framework.
And here is my test case:
it('test', async function () {
let value;
for(let i = 0; i <= 300; i++) {
await this.Test.getValue.call([i]);
}
});
The test case will be fail when the i is equal to 256.
In basically, the input only accept uint256 type in my case. But it also accept array.
Here is the error message when test case fail:
Error: invalid number value (arg="_value", coderType="uint256", value=[256])
Does anyone know the reason?

How to set default parameters to functions in Solidity

I came across below example from the Solidity Documentation and have similar code in my project and want to set default value to key parameter if the key is not passed from the caller
pragma solidity ^0.4.0;
contract C {
function f(uint key, uint value) public {
// ...
}
function g() public {
// named arguments
f({value: 2, key: 3});
}
}
My questions are -
Do Solidity language provides default parameters?
How to achieve the same if default parameters are not allowed then?
Appreciate the help?
Solidity does not support default parameters, but it is on their roadmap (see https://github.com/ethereum/solidity/issues/232). To work around this, just use function overloading:
pragma solidity ^0.4.0;
contract C {
function f(uint key, uint value) public {
// ...
}
function h(uint value) public {
f(123, value);
}
function g() public {
// named arguments
f({value: 2, key: 3});
}
function i() public {
h({value: 2});
}
}
Openzeppelin does a great job exemplifying how you can make "default" arguments. Check out their SafeMath Library.
In it, they have two sub (subtraction) contracts that are identical visibility and mutability wise- but a key difference:
function sub(
uint256 a,
uint256 b
)internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
The first one by default takes two arguments a & b (which will be subtracted). If a third argument is not given (the error statement) it will default to
SafeMath: subtraction overflow
If a third argument is given, it will replace that error statement.
Essentially:
pragma solidity >=0.6.0 <0.9.0;
contract C {
function f(unit value) public {
uint defaultVal = 5;
f(defaultVal, value);
function f(uint key, uint value) public {
// ...
}
function g() public {
// named arguments
f(2, 3);
}
}