Assigning different-sized array literals to the same variable in Solidity - solidity

Given something like this:
struct Strings {
string[] s;
}
Strings[] memory strings = new Strings[](2);
I'm trying:
strings[0].s = ["a","b"];
strings[1].s = ["a","b","c"];
Which is giving me:
TypeError: Type string memory[2] memory is not implicitly convertible
to expected type string memory[] memory
TypeError: Type string memory[3] memory is not implicitly convertible
to expected type string memory[] memory
What's the most elegant way to make this work? Should I initialize s?

You're trying to assign a fixed-size array ["a","b"] into a dynamic-size array string[] s.
For each of the properties s, you need to predetermine the length of the dynamic size array beforehand, and then assign the values one by one.
pragma solidity ^0.8;
contract MyContract {
struct Strings {
string[] s;
}
function foo() external pure {
Strings[] memory strings = new Strings[](2);
strings[0].s = new string[](2);
strings[0].s[0] = "a";
strings[0].s[1] = "b";
strings[1].s = new string[](3);
strings[1].s[0] = "a";
strings[1].s[1] = "b";
strings[1].s[1] = "c";
}
}

Related

Failed to call solidity function with array argument

I'm trying to execute contract's function use abi encode and call function, but I'm getting an error. Don't know how to resolve this issue.
pragma solidity >=0.8.0;
contract AbiTest {
event A(uint);
event Out(bytes);
function test() public {
bytes4 func = bytes4(keccak256("callMe(uint[])"));
uint[] memory arr = new uint[](3);
arr[0] = 3;
arr[1] = 4;
(bool res, bytes memory data) = address(this).call(abi.encode(func, arr));
emit Out(data);
require(res);
}
function callMe(uint[] memory array) public {
emit A(array[0]);
emit A(array[1]);
}
}
This solution works well, if arguments sizes are known at compile time, but with dynamic sizes this doesn't work.
How can I resolve it?
I think this solution should work just fine. This is compatible with dynamic array lengths.
function test() public {
uint[] memory arr = new uint[](2);
arr[0] = 3;
arr[1] = 4;
(bool success, ) = address(this).call
(
abi.encodeWithSignature(
"callMe(uint[])",
arr
)
);
}
Need to use abi.encodePacked see Solidity doc instead of abi.encode because second one don't compress items and therefore uses 32 bytes for the function name.
pragma solidity >=0.8.0;
contract Abi2Test {
function getByte() public returns (bytes memory) {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
return abi.encode(func);
}
function getByte2() public returns (bytes memory) {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
return abi.encodePacked(func);
}
}
getByte output: 0x6600981c00000000000000000000000000000000000000000000000000000000
getByte2 output: 0x6600981c
Therefore, when you use encode with function signature additionally will be allocated 28 bytes and EVM can't correctly parse passed value.
So, to correctly call function with abi.encode and abi.encodePacked you need to pack arguments with encode at the first, and at the second group function's signature with encoded bytes.
pragma solidity >=0.8.0;
contract Abi2Test {
event A(uint256);
event Out(bytes);
event Out1(bytes);
function test() public {
bytes4 func = bytes4(keccak256("callMe(uint256[])"));
uint256[] memory arr = new uint256[](3);
arr[0] = 3;
arr[1] = 4;
(bool res, bytes memory data) = address(this).call(abi.encodePacked(func, abi.encode(arr)));
emit Out(data);
require(res);
}
function callMe(uint256[] memory array) public {
emit A(array.length);
emit Out1(msg.data);
}
}

There is Array.push function in solidity how it is return length of the array?

pragma solidity >=0.5.0 <0.6.0;
contract ZombieFactory {
event NewZombie(uint zombieId, string name, uint dna);
uint dnaDigits = 16;
uint dnaModulus = 10 ** dnaDigits;
struct Zombie {
string name;
uint dna;
}
Zombie[] public zombies;
function _createZombie(string memory _name, uint _dna) private {
uint id = zombies.push(Zombie(_name, _dna)) - 1; //**here how it is return length of array**
emit NewZombie(id, _name, _dna);
}
function _generateRandomDna(string memory _str) private view returns (uint) {
uint rand = uint(keccak256(abi.encodePacked(_str)));
return rand % dnaModulus;
}
function createRandomZombie(string memory _name) public {
uint randDna = _generateRandomDna(_name);
_createZombie(_name, randDna);
}
}
I want to know how it is working i tired to searched in google but i cant get exact results try to explain how it returning length of array and basically it just add the element ...
before solidity v0.6.
Arrays have a member "push" define as :
Dynamic storage arrays and bytes (not string) have a member function called push that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length.
It's changed after v0.6.
Reference https://docs.soliditylang.org/en/v0.8.12/060-breaking-changes.html

How to pass array of struct of type voter in solidity?

I want to pass an array of struct to a function. The array is of type voter. So below is my code. But I am getting error as
UnimplementedFeatureError: Copying of type struct Election.voter memory[] memory to storage not yet supported.
struct voter{
string name;
address constituency;
uint age;
bool isVoted;
}
struct constituency{
string name;
voter[] candidates;
}
constituency[] public constituencyRegister;
function addConstituency(string memory _name,voter[] memory _candidates)
public
{
constituency memory c = constituency(_name, _candidates);
constituencyRegister.push(c);
}
In the following line you are trying to assign a memory array of struct to a storage array of struct, since c contains _candidates:
constituencyRegister.push(c);
Instead, you could push to a storage var, get its reference and only then assigning the name and pushing each _candidates element to the array:
pragma solidity ^0.8.0;
contract MuC {
struct voter{
string name;
address constituency;
uint age;
bool isVoted;
}
struct constituency{
string name;
voter[] candidates;
}
constituency[] public constituencyRegister;
function addConstituency(string memory _name,voter[] memory _candidates)
public
{
constituency storage c = constituencyRegister.push();
c.name = _name;
for(uint i = 0; i < _candidates.length; i++) {
c.candidates.push(_candidates[i]);
}
}
}
This is compiling for me with 0.8.9.

Send oraclize query with variables

I need to send a solidity request, which looks like this:
validId[oraclize_query(60, "URL", "json(http://myIp.something/wallet_check.json?wallet=".toSlice().concat(toString(msg.sender).toSlice()).toSlice().concat(").white_listed".toSlice()) )] = msg.sender;
However, conversion toString return unreadable string, which is probably why oracle can't handle the request. How do i convert address to string or go around the conversion?
code for toString():
function toString(address x) returns (string) {
bytes memory b = new bytes(20);
for (uint i = 0; i < 20; i++)
b[i] = byte(uint8(uint(x) / (2**(8*(19 - i)))));
return string(b);
}

Array allocate in function

I ve got a problem with allocating cli::array in function.
I have this kind of object:
array<double>^ tmsr2;
now I want to allocate it in function so:
void allocate(array<double>^ tmsr2)
{
tmsr2=gcnew array<double>(100);
}
Now, tmsr2 in function gets allocated well but I lose the pointer when returning to main()
The problem is clear to me, just like if I want to allocate simple array "double *a"; I need to pass pointer to function so "&a" and then everything works fine. I just don't know the syntax with managed arrays. Help much appreciated.
Peter
Since array<double> is a managed type, you can use a managed tracking reference here, instead of a plain reference.
void allocate(array<double>^% tmsr2)
{
tmsr2 = gcnew array<double>(100);
}
Here's my test app:
public ref class AsAClassField
{
public:
array<double>^ b;
AsAClassField()
{
allocate(b);
Debug::WriteLine("b = " + (b != nullptr ? "array" : "null"));
}
};
int main(array<System::String ^> ^args)
{
array<double>^ a = nullptr;
allocate(a);
Debug::WriteLine("a = " + (a != nullptr ? "array" : "null"));
AsAClassField^ foo = gcnew AsAClassField();
return 0;
}
Output:
a = array
b = array
Of course, you could always switch your allocate function to return the newly allocated array, rather than taking it as a reference. That would be more in the managed style.
You can pass the array as a reference:
void allocate(array<double>^ &tmsr2)
{
tmsr2=gcnew array<double>(100);
}