It is mentioned here that ABIEncoderV2 should make it possible to pass structs from contract to contract. Solidity latest ABI spec mentions that it is possible to have tuple array as a input. Can you please say how array of structs / tuple[] should be sent in below example code from TupleArrayFactory to TupleArray?
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TupleArrayFactory {
TupleArray newTuple;
function createTuple() public {
newTuple = new TupleArray("trait0", "display0", 0);
// newTuple.pushAttribute([["trait1","display2",2]]);
newTuple.pushAttribute(["trait1","display2",2]);
// TypeError: Unable to deduce common type for array elements.
}
}
contract TupleArray {
struct Attribute {
string trait_type;
string display_type;
uint8 value;
}
Attribute[] public attributes;
Attribute[] public tempAttributeArr;
constructor(string memory trait_type, string memory display_type, uint8 value) payable {
Attribute memory Attribute0 = Attribute(trait_type, display_type, value);
tempAttributeArr.push(Attribute0);
pushAttribute(tempAttributeArr);
//pushAttribute([trait_type, display_type, value]);
// TypeError: Unable to deduce common type for array elements.
}
function pushAttribute(Attribute[] memory _attr) public payable {
attributes.push(_attr[0]);
}
}
Here's a working version of your example:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract TupleArrayFactory {
TupleArray newTuple;
function createTuple() public {
newTuple = new TupleArray("trait0", "display0", 0);
TupleArray.Attribute[] memory attributes = new TupleArray.Attribute[](1);
attributes[0] = TupleArray.Attribute("trait1", "display2", 2);
newTuple.pushAttribute(attributes);
}
}
contract TupleArray {
struct Attribute {
string trait_type;
string display_type;
uint8 value;
}
Attribute[] public attributes;
constructor(string memory trait_type, string memory display_type, uint8 value) payable {
Attribute[] memory tempAttributeArr = new Attribute[](1);
tempAttributeArr[0] = Attribute(trait_type, display_type, value);
pushAttribute(tempAttributeArr);
}
function pushAttribute(Attribute[] memory _attr) public payable {
attributes.push(_attr[0]);
}
}
Some remarks:
pushAttribute([["trait1","display2",2]]) won't work because an array is not implicitly convertible to a struct. You have to invoke the struct constructor.
Even then pushAttribute([TupleArray.Attribute("trait1","display2",2)]) won't work because Solidity currently does not have dynamic array literals. If you want to pass a dynamic array into a function you have to create a variable for it.
Your pushAttribute() takes an array but ignores all but the first element. So why make it an array at all? I'm assuming it's for the sake of the example but if not, you should make the function just accept a single struct.
Putting tempAttributeArr in storage works but is not necessary. You can put it in memory, which is cheaper.
Solidity latest ABI spec mentions that it is possible to have tuple array as a input.
The docs you are linking to only describe how tuples (used for example to return named function arguments or return values) are formatted in the JSON describing the ABI.
Maybe you're referring to some other section on that page? In general, structs are encoded as tuples the ABI, which might be the source of confusion there. But this simply means that if you are encoding the parameters yourself (e.g. when you use off-chain code to issue a transaction), you should encode structs as tuples. If you are just calling external functions on chain, you do not have to do that. In fact you can't because structs and tuples are not convertible to each other. You have to use an actual struct when a function expects a struct and the struct type must be the exact one specified in function signature.
Related
So apparently we can not use dynamic arrays while using memory data location. But the following code gives me error:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract A {
uint256[] public numbers;
constructor(uint256[] memory _numbers) {
for(uint256 i=0; i<_numbers.length; i++) {
numbers.push(_numbers[i]);
}
}
function get() public view returns (uint256[] memory) {
return numbers;
}
}
contract Manager {
function makeA() public returns(uint256) {
uint256[10] memory numbers;
// push is not supported for memory data location of array
numbers[0] = 10;
A a = new A(numbers); //Error: Invalid implicit conversion from uint256[10] memory to uint256[] memory requested
return a.numbers(0);
}
}
I solved it using this syntax of declaring static array:
uint256[] memory numbers = new uint256[](5);
Although it solved the issue but I am still confused behind the concept of why the later works? My assumption is that solidity differs the type between uint256[] and uint256[10]. Correct me if I am wrong, also an explanation of this behavior will be helpful.
The error says cannot convert static array to dynamic array implicitly.
In Java, whenever you pass Integer wrapper to int or int to Integer, you don't have to explicitly convert it. the compiler does it for you. In the same way, some conversion happens at the compiler level known as implicit conventions.
In EVM, when you passed numbers[10] that says that you are declaring a static type array. In the constructor, you are pushing it into a dynamic array. compiler by default cannot convert it into unit[10] to unit[]. this is what the error says.
when you declare that with help of a new keyword it is one way to declare a dynamic array. so, the dynamic array is passed and values will be inserted into the dynamic array. so, no conversion is required.
if you feel, the answer needs to be updated please update it.
The difference is as follows:
Firstly, a static array will have its memory slots allocated when defined.
Yet dynamic arrays will require a new allocation to be issued every time you push a new element into it.
This might not seem like a big thing in a normal OOP language, but Solidity requires gas per instruction, and that's where that difference can't be ignored.
Can someone please explain me why this works fine
import "./SimpleStorage.sol";
pragma solidity ^0.6.0;
contract storageFactoryContract {
SimpleStorage[] public asd;
function createSimpleStorageContract() public{
SimpleStorage simpleStorage = new SimpleStorage();
}
}
but this doesn't
import "./SimpleStorage.sol";
pragma solidity ^0.6.0;
contract storageFactoryContract {
function createSimpleStorageContract() public{
SimpleStorage[] public asd;
SimpleStorage simpleStorage = new SimpleStorage();
}
}
The error is:
freecodecamptutorial/FactoryContract.sol:14:26: ParserError: Expected ';' but got 'public'
SimpleStorage[] public asd;
^----^
In the second snippet, you're trying to set a visibility modifier for a regular variable. Only state variables (i.e. contract properties) and functions can have visibility modifiers.
Reference type variables (such as a contract) need to also have a data location. Since you're not working with the variable further, you can safely use the memory location. If you were to store it in storage, you might want to use the storage data location.
function createSimpleStorageContract() public {
// removed the `public` modifier
// added the `memory` data location
SimpleStorage[] memory asd;
SimpleStorage simpleStorage = new SimpleStorage();
}
This will generate two warnings about the unused variables asd and simpleStorage. It's simply because you're assigning the variables but never using them later. In this context, you can safely ignore them.
Note: I'm assuming that the SimpleStorage is a contract without a constructor or with a 0-argument constructor. Otherwise, you might get some different errors, for example wrong argument count.
I have this piece of code, but I dont have implemented a view returns function.
mapping (uint256 => TokenTimeLockInfo) public locks;
struct TokenTimeLockInfo {
Token token;
address beneficiary;
uint256 amount;
uint256 unlockTime;
}
I can access the value way web3js are possible?
Or do I need another contract to implement a view returns?
Since the property is public, you can access it using the getter function automatically generated by Solidity compiler.
const myContract = new web3.eth.Contract(abiJson, contractAddress);
// returns value of the mapping for the key `0`
const info = await myContract.methods.locks(0).call();
Docs:
https://docs.soliditylang.org/en/v0.8.6/contracts.html#getter-functions
https://web3js.readthedocs.io/en/v1.3.4/web3-eth-contract.html#methods-mymethod-call
I tried to compile my code, but I got the following error:
TypeError: Data location must be "memory" for parameter in function,
but none was given
my code:
pragma solidity ^0.5.0;
contract memeRegistry {
string url;
string name;
uint timestamp;
function setmeme(string _url,string _name, uint _timestamp) public{
url = _url;
name = _name;
timestamp = _timestamp;
}
}
Explicit data location for all variables of struct, array or mapping types is now mandatory. This is also applied to function parameters and return variables.
Add memory after string
function setmeme(string memory _url, string memory _name, uint _timestamp) public{
check here for Solidity 0.5.0. changes https://solidity.readthedocs.io/en/v0.5.0/050-breaking-changes.html
//The version I have used is 0.5.2
pragma solidity ^0.5.2;
contract Inbox{
string public message;
//**Constructor** must be defined using “constructor” keyword
//**In version 0.5.0 or above** it is **mandatory to use “memory” keyword** so as to
//**explicitly mention the data location**
//you are free to remove the keyword and try for yourself
constructor (string memory initialMessage) public{
message=initialMessage;
}
function setMessage(string memory newMessage)public{
message=newMessage;
}
function getMessage()public view returns(string memory){
return message;
}
}
It's working for me.
string memory firstname
Select a different version of the solidity compiler. ^0.4.25 works for me.
The version of the solidity compiler has to be set both on the file and in the compile tab on remix(it is a drop-down menu).
You need to make the return parameters explicitly memory:
Thus,
function setmeme(string _url,string _name, uint _timestamp) public
Becomes
function setmeme(string memory _url, string memory _name, uint _timestamp) public{
When calling trying to pass a string from contract to contract, I get an error. in getName with error. I'm aware you cannot pass strings but what is the reason?
Return argument type inaccessible dynamic type is not implicitly
convertible to expected type (type of first return variable) string
memory. return toBeCalled.getName();
pragma solidity ^0.1.0;
contract ToContract{
FromContract fromContract = new FromContract();
function getName() constant returns (string) {
return fromContract.getName();
}
}
contract FromContract{
string name = 'dapp';
function getName() constant return(string){
return name;
}
}
In solidity a string "Hey" is internally stored as ["H","e","y"] which is a dynamic array. As of now it has no support to pass dynamic arrays. you can try to work around this by passing the string as a part of an object.