When I try to do this in Solidity, it gives me
UnimplementedFeatureError: Nested dynamic arrays not implemented here.
I see this in code examples. Does Solidity not support this?
Edit - posted complete code below. So I figured out the problem is only in the last function. It doesn't like that I am returning a dynamic array. How could I implement the same functionality (I want to return a string array of data)?
pragma solidity ^0.4.6;
contract EmailIntegrity {
// Map an array of EmailIntegrityStructs for each email address.
// The first element will be used for the integrity record.
// The rest will be used for audit records.
enum ItemType { Integrity, Audit }
struct EmailIntegrityStruct {
ItemType itemType;
uint timestamp;
string data;
}
mapping(address => EmailIntegrityStruct[]) emailIntegrityStructs;
function hasEmailIntegrityData(address emailAddress) public constant returns(bool isEmail) {
return emailIntegrityStructs[emailAddress][0].timestamp == 0;
}
function insertIntegrityData(address emailAddress, uint timestamp, string data) public returns(bool success) {
if (hasEmailIntegrityData(emailAddress)) {
revert();
}
EmailIntegrityStruct memory integrityData;
integrityData.itemType = ItemType.Integrity;
integrityData.timestamp = timestamp;
integrityData.data = data;
emailIntegrityStructs[emailAddress].push(integrityData);
return emailIntegrityStructs[emailAddress].length == 1;
}
function insertAuditData(address emailAddress, uint timestamp, string data) public returns(bool success) {
if (!hasEmailIntegrityData(emailAddress)) {
revert();
}
EmailIntegrityStruct memory auditData;
auditData.itemType = ItemType.Audit;
auditData.timestamp = timestamp;
auditData.data = data;
emailIntegrityStructs[emailAddress].push(auditData);
return emailIntegrityStructs[emailAddress].length > 1;
}
function getIntegrityData(address emailAddress) public constant returns(string data) {
if (!hasEmailIntegrityData(emailAddress)) {
revert();
}
return emailIntegrityStructs[emailAddress][0].data;
}
function getAuditData(address emailAddress) public constant returns(string[] data) {
if (!hasEmailIntegrityData(emailAddress)) {
revert();
}
var length = emailIntegrityStructs[emailAddress].length;
string[] memory auditData = new string[](length - 1);
for (uint i = 1; i < length ; i++) {
auditData[i] = emailIntegrityStructs[emailAddress][i].data;
}
return auditData;
}
}
Solidity and Javascript allow nested arrays but we do not have the ability to pull a nested dynamic array from the solidity over to javascript world.
it is the limitation of the bridge between solidity and javascript. Since strings inside of solidity are represented as dynamic arrays, we cannot transfer array of strings.
UPDATE
Since then, solidity has evolved, you can return dynamic array including structs. A test contract:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Test {
struct User{
string name;
string LastName;
}
User[] public store;
function addToStorage() external returns (User[] memory ) {
User memory newUser=User(
"yilmads","bin"
);
store.push(newUser);
return store;
}
}
This compiled fine for me.
I added a definition of MyStruct and upped the Solidity version to the current one (though it compiled fine with the old pragma too). Here's the code I compiled via Remix:
pragma solidity ^0.4.19;
contract MyContract{
struct MyStruct {
uint256 foo;
}
mapping(address => MyStruct[]) myStruct;
}
Could it be that you're using an older version of the Solidity compiler? (The ^0.4.6 made me think that perhaps you are.)
What's your MyStruct? Perhaps a more interesting structure there would fail. In general, please try to provide full code samples that reproduce the problem you're seeing.
Related
I have a basic contract that looks like this:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
pragma abicoder v2;
contract testStruct {
struct Example {
uint256 number;
address user;
}
Example public example;
function set_struct(Example memory _example) public {
example = _example;
}
}
I have tried using this sdk like this.
//Create the transaction
const transaction = new ContractExecuteTransaction()
.setContractId(newContractId)
.setGas(100_000_000)
.setFunction("set_struct", new ContractFunctionParameters()
.addBytesArray(byte[][] structInBytes)
Some data type are not presented in the SDK. How can I call a smart contract and pass a complex type like a struct ?
If you are using structs as inputs/outputs to your contracts, or data types that aren't supported by the SDKs, you should check this example repo out which shows how to use commonly known libraries (web3.js, ethers.js, web3.j and abiDecode.js) to encode and decode contract function parameters and results https://github.com/hashgraph/hedera-smart-contracts-libs-lab
For example:
// get the constructor parameter
// .slice(2) to remove leading '0x'
const constructParameterAsHexString = abiInterface.encodeDeploy([constructMessage]).slice(2);
// convert to a Uint8Array
const constructorParametersAsUint8Array = Buffer.from(constructParameterAsHexString, 'hex');
Is it possible to return an array in solidity?
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
pragma experimental ABIEncoderV2;
contract Test {
constructor() {}
function multiArray() external pure returns (uint256[2][] memory doubleArray) {
doubleArray[0] = [uint(12345678),uint(1234567)];
return doubleArray;
}
}
this does not work, but I thought with solidity > 0.8.0 it should be possible, also with "ABIEncoderV2"?
Nested arrays are still incomplete but they work at a certain level.
Currently, nested arrays can only be used within an internal call to functions that are either private or internal. They should also be stored in storage or memory.
The biggest issue is using them in the calldata as a parameter or return statement which is not yet supported.
I'll recommend trying to find another way to return the nested array information like a mapping, struct, or even returning multiple lists and then working with them inside the function.
Another alternative is to pass the data as bytes and extract the array data or vice-versa. However, this can have a high gas cost on large arrays and some other problems. To use this method you can take advantage of the new ABI encoder.
Solidity support 1D array only. Multidimensional arrays are yet to come.
// SPDX-License-Identifier: GPL-3.0;
pragma solidity >=0.7.0 <0.9.0;
contract Test{
string[] A=["A","B"];
function fun() public view returns(string[] memory){
return A;
}
}
Try to initialize the array first and then return values by index. You can also use a mapping, but keep in mind you can't easily iterate over it.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract NestedArrays{
mapping(uint256 => uint256[]) mappingArray;
uint256[2][] multiArray;
constructor(){
multiArray.push([1,2]);
multiArray.push([3,4]);
mappingArray[0] = [10,20];
mappingArray[1] = [30,40];
}
function getMultiArray() public view returns(uint256[2][] memory){
return multiArray;
}
function getMultiArrayMember(uint256 index) public view returns(uint256[2] memory){
require(multiArray.length > index, "wrong index");
return multiArray[index];
}
function getMappingArrayMember(uint256 index) public view returns(uint256[] memory){
return mappingArray[index];
}
}
here is my code:
pragma solidity 0.7.4;
pragma experimental ABIEncoderV2;
mapping(uint => ProductWithBids) public internalProducts;
struct SecretBids {
uint values;
bool fake;
string secret;
}
struct ProductWithBids {
uint id;
string name;
uint price;
address payable owner;
address payable beneficiary;
bool purchased;
mapping(address => Bid[]) bids;
mapping(address => SecretBids[]) nakedBids;
// Allowed withdrawals of previous bids
mapping(address => uint) pendingReturns;
uint bidsCount;
bool ended;
uint biddingEnd;
uint revealEnd;
address highestBidder;
uint highestBid;
}
function getNakedBids(uint _id)
public
view
returns(SecretBids[] memory product) {
ProductWithBids storage selectedProduct = internalProducts[_id];
return selectedProduct.nakedBids;
}
i need to return the nakedBids but i get the following error :
TypeError: Return argument type mapping(address => struct eShop.SecretBids storage ref[] storage ref) is not implicitly convertible to expected type (type of first return variable) struct eShop.SecretBids memory[] memory.
return selectedProduct.nakedBids;
I get what the error means, i think i have tried everything and looked everywhere. From what i have found is that solidity doesn't support at all this action .Does anyone has any idea what i can try next? If i missed any other important code parts let me know. Thanks in advance.
I also tried :
returns(mapping(address => SecretBids[]) memory nakedBids)
but then i get the following error:
Types containing (nested) mappings can only be parameters or return variables of internal or library functions.
returns(mapping(address => SecretBids[]) memory nakedBids) {
^-----------------------------------------------^
,TypeError: Type mapping(address => struct eShop.SecretBids[]) is only valid in storage because it contains a (nested) mapping.
returns(mapping(address => SecretBids[]) memory nakedBids)
ps: if i change it to storage it complains that Data location must be memory.
By the way, you should move mapping(address => SecretBids[]) nakedBids; out of the struct, and let it be a storage variable on its own.
nakedBids is an array of SecretBids struct.
My answer is based on solidity v0.5.x (hopefully similar to 0.7.4). Return array of struct is not supported in solidity yet. Instead you can return tuple.
returns (uint[] memory, bool[] memory, string[] memory)
I pretty much gave up on the idea of creating a getter. So i got the data i needed in a more direct way. Probably is not the best way but it worked for me. Feel free to share any other solutions.
Here is the code:
function reveal(
uint _id
)
public
payable
{
ProductWithBids storage selectedProduct = internalProducts[_id];
//onlyAfter biddingEnd && onlyBefore revealEnd
require(block.timestamp > selectedProduct.biddingEnd);
require(block.timestamp < selectedProduct.revealEnd);
uint count = selectedProduct.bidsCount;
uint[] memory values = new uint[](count);
bool[] memory fake = new bool[](count);
string[] memory secret = new string[](count);
for (uint i = 0; i < count; i++) {
values[i] = selectedProduct.nakedBids[msg.sender][i].values;
fake[i] = selectedProduct.nakedBids[msg.sender][i].fake;
secret[i] = selectedProduct.nakedBids[msg.sender][i].secret;
}
revealInternal(
values,
fake,
secret,
_id
);
}
I did not go from the start with this solution because i needed the react-js to do it from the front-end and then just pass it in revealInternal(...);
I am currently working on my first smart contract. After already reading a few topics out there I still don't have a clue on how to initialize the string array inside my struct.
The idea is that the customer can always choose from a variety of energysources on a homepage which then gets deployed on the smart contract if he's done choosing.
So what would be the best/efficient way to implement this idea onto the blockchain?
pragma solidity ^0.4.23; /* solhint-disable */
contract EnergyContract {
struct EnContract {
uint price;
uint amount;
string[] energysource;
address creator;
}
EnContract[] aContract;
function createContract(uint _price, uint _amount, address _creator) public {
aContract.length += 1;
EnContract storage _EnContract = aContract[aContract.length - 1];
_EnContract.price = _price;
_EnContract.amount = _amount;
_EnContract.creator = _creator;
}
}
The way you declare your array is correct. You don't need to do anything else. You can just start pushing data into the array.
I have a struct Purchase in which I'm putting an array of payments. However, when I try to add the new payments array in my makePayment method I get an error back from the solidity compiler: "Internal compiler error: Copying of type struct Payment memory[] memory to storage not yet supported." When I change the mayment array to be storage or memory, I get the same error. I've added the relevant code below.
Is it possible to do what I'm trying to do in solidity? I don't see anything explicitly saying it's not possible in the documentation but I also don't see any examples doing what I'm trying to do. :|
struct Payment {
address maker;
uint amount;
}
struct Purchase {
uint product_id;
bool complete;
Payment[] payments;
}
Purchase[] purchases;
function makePayment(uint product_id, uint amt, uint purchase_id) returns (bool) {
Payment[] payments;
payments[0] = Payment(address, amt);
purchases[purchase_id] = Purchase(product_id, false, payments);
}
You need to manually change the length of the payments array when setting it.
Either use:
Payment[] payments;
payments[payments.length++] = Payment(address, amt);
Or:
Payment[] payments;
payments.push(Payment(address, amt));
For setting the payments array in Purchase, instead of creating an array and trying to set it to the Purchase.payments you can do the following:
uint purchase_id = purchases.length++;
purchases[purchase_id].product_id = product_id;
purchases[purchase_id].complete = false;
purchases[purchase_id].payments.push(Payment(msg.sender, amt));
Extending the purchases length will automatically create the new attributes. Then you can set them manually.
I found this as best solution.
event OnCreateRoom(address indexed _from, uint256 _value);
struct Room {
address[] players;
uint256 whosTurnId;
uint256 roomState;
}
Room[] rooms;
function createRoom() public{
address[] adr;
adr.push(msg.sender);
Room memory room = Room(adr, 0, 0);
rooms.push(room);
OnCreateRoom(msg.sender, 0);
}