Copying of type struct.struct memory[] memory to storage not yet supported - solidity

I am working on a Solidity smart contract and I want to store an array of structs inside another struct. Here are my structs:
struct Product {
string productId;
string cabineNumber;
string productName;
string country;
string factoryNumber;
string productWeight;
string productAmount;
}
struct Form {
uint256 Id;
string purchId;
string customerName;
string customerPhone;
string contractNumber;
Product[] products;
uint256 created;
uint256 updated;
}
As you can see, the Form struct contains an array of Product structs called products.
I have a function to initialize a Product struct:
function initProduct(string[] memory product) public returns (Product memory) {
Product memory myproduct;
myproduct.productId = product[0];
myproduct.cabineNumber = product[1];
myproduct.productName = product[2];
myproduct.country = product[3];
myproduct.factoryNumber = product[4];
myproduct.productWeight = product[5];
myproduct.productAmount = product[6];
return myproduct;
}
I also have a function to create a Form struct:
function createForm(Form memory form, string[] memory product) public returns (bool) {
Product memory productEvi;
productEvi = initProduct(product);
Product[] memory products = new Product[](1);
products[0] = productEvi;
forms.push(Form(
form.Id,
form.purchId,
form.customerName,
form.customerPhone,
form.contractNumber,
products, // Error: copying of type struct Product[] memory to storage not yet supported
form.created,
form.updated
));
uint256 index = findFormIndex(form.Id);
forms[index].updated = block.timestamp;
forms[index].created = block.timestamp;
return true;
}
However, I am getting the error "Copying of type struct Product[] memory to storage not yet supported" when trying to store the products array in the Form struct. How can I fix this error and store an array of Product structs inside a Form struct?
I tried to store multiple instances of a struct Product inside another struct Form and push the Form object to an array. I expected the code to compile without any errors and to be able to store and retrieve multiple Product objects within a Form.

Related

Solidity unit type holding an array?

I am working through the solidity course cryptozombies and heres something im not understanding
struct Zombie {
string name;
uint dna;
uint32 level;
uint32 readyTime;
uint16 winCount;
uint16 lossCount;
}
Zombie[] public zombies;
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
function _createZombie(string memory _name, uint _dna) internal {
uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
emit NewZombie(id, _name, _dna);
}
Based on my understanding "zombies" is an array containing a string and different type of integers. If you look in the _createzombie function "id" is set as an uint. How can something of type uint store all these values?
Based on my understanding "zombies" is an array containing a string and different type of integers
This is only partially correct. zombies is in fact an array. But each of its items is of type Zombie.
A struct (docs) is kind of a "wrapper" type - it can contain multiple other datatypes on the inside, but from the outside it's seen as just one datatype (in this case you created a new datatype called Zombie).
You can see it for example in the push() function that accepts one new item of the array - type Zombie (wrapping the other variables).
zombies.push(
Zombie(...) // pushes 1 item of type `Zombie` to the array
)

get String value not as hex from smartcontract

I got a smartcontract function returning several values on of them as string
I've trying to transform this number to UTF with web3 and is not working.
If I try it on remix calling the smart contract it's works fine... but not from JS
my current code in the smart contract are
function getToken(uint256 _tokenId) public view returns (
string _tokenName,
string _cryptoCardID,
uint256 _price,
uint256 _nextPrice,
address _owner
) {
_tokenName = doggies[_tokenId].name;
_cryptoCardID = doggies[_tokenId].cryptoCardID;
_price = tokenIdToPrice[_tokenId];
_nextPrice = nextPriceOf(_tokenId);
_owner = tokenIdToOwner[_tokenId];
}
from JS
for ( var i = 0; i < result; i++){
myfunction.getToken.call( i , function (error, resultGetAll) {
console.log("Token ID:"+i+ "Data:"+JSON.stringify(resultGetAll)); //here i get _cryptoCardID as hex 0x000000...
}) // each one
}// for
what I expected is a string in readable form (utf8)
Contracts will store the binary data, so returning the data in hexadecimal form is a more efficient way of viewing this data.
You can always decode the hex to utf-8 with a utility function from the web3 library.
Note: this method may differ depending on your web3 version, see here for web3.utils.hexToUtf8 alternative.
Example:
$ web3.toUtf8("0x68656c6c6f20776f726c640000000000000000000000000000000000000000")
$ "hello world"
tested on web3 version: 0.20.3

Set a default value in constructor

Ok. So I know this should be easy to do, I simply want to set a default value in the following:
uint8 public gasPriceLimit; //Gas Price Limit
//Constructor
constructor(string _name, string _symbol, uint8 _decimals) public {
name = _name;
symbol = _symbol;
decimals = _decimals;
uint8 gasPriceLimit = 999;
}
However, doing this I get the following error when compiling:
Type int_const 999 is not implicitly convertible to expected type uint8.
I also tried setting in the declaration itself without luck.
Cheers
Ok. A mystery to me, but if I change to use uint instead of uint8 then the following works fine:
uint public gasPriceLimit = 999;

Stack too deep, try using less variables on creating contract

I am trying to create a contract from a contract factory using the following function:
function createContract(string _var1, string _var2,
uint32 _var3, string _var4, string _var5,
string _var6, uint32 _var7, uint32 _var8, uint32 _var9,
uint32 _var10, uint32 _var11)
public returns (address contractAddress) {
return new Contract(_var1, random1, random2, _var2,
_var3, _var4, _var5, _var6, _var7, _var8,
_var9, _var10, _var11);
}
N.B. random1 and random2 are fields in the contract factory.
This function throws Stack too deep, try using less variables. I have read that I should split up the function etc. to get around this, but obviously, that is not an option here. Is there a way to get this working?
So, initially I tried grouping the variables by type into this:
function createContract(string[] _var1, uint32[] _var2)
public returns (address contractAddress) {
return new Contract(_var1, random1, random2, _var2);
}
However, nested dynamic arrays are not supported at this time. As string is represented as byte[] in EVM, a string[] is in fact passed as a byte[][].
I ended up grouping the uint32 and leaving the strings:
function createContract(string _var1, string _var2, uint32[] _var3,
string _var4, string _var5, string _var6)
public returns (address contractAddress) {
return new Contract(_var1, random1, random2, _var2,
_var3, _var4, _var5, _var6);
}
EDIT: Even though this method works, it is badly designed. See my other answer for a better workaround for this.
I went one step further with this as having an array of unit32 is ambiguous and confusing in terms of the position of a specific argument.
My final attempt utilized the struct type to provide a less ambiguous implementation. This struct is in a library in a separate file Library.sol:
struct ContractArgs {
uint32 var1;
string var2;
uint32 var3;
....
}
The factory method looks like this:
function createContract(Library.ContractArgs _contractArgs)
public returns (address contractAddress) {
return new Contract(_contractArgs, random1, random2);
}
And my constructor looks like this:
function Contract(Library.ContructorArgs _contractorArgs,
uint32 _supplierId, string _supplierName) {
contractArgs = _contractArgs;
random1 = _random1;
random2 = _random2;
}

StructureToPtr not copying primitive type fields in native struct to ref struct correctly

I have a native struct, (which is quite large so I have to use new key word to instantiate, below is just to make a MCVE I cant change the struct as it is provided as external dependencies),
struct NativeStruct
{
char BrokerID[11];
char InvestorID[13];
char InstrumentID[31];
char OrderRef[13];
char UserID[16];
char OrderPriceType;
char Direction;
double LimitPrice;
}
I want to convert NativeStruct to managed object, so I defined a ref struct to mirror it, this also used two enums as below,
public enum struct EnumOrderPriceTypeType
{
AnyPrice = (Byte)'1',
LimitPrice = (Byte)'2',
BestPrice = (Byte)'3',
LastPrice = (Byte)'4',
LastPricePlusOneTicks = (Byte)'5',
LastPricePlusTwoTicks = (Byte)'6',
LastPricePlusThreeTicks = (Byte)'7',
AskPrice1 = (Byte)'8',
AskPrice1PlusOneTicks = (Byte)'9',
AskPrice1PlusTwoTicks = (Byte)'A',
AskPrice1PlusThreeTicks = (Byte)'B',
BidPrice1 = (Byte)'C',
BidPrice1PlusOneTicks = (Byte)'D',
BidPrice1PlusTwoTicks = (Byte)'E',
BidPrice1PlusThreeTicks = (Byte)'F'
};
public enum struct EnumDirectionType
{
Buy = (Byte)'0',
Sell = (Byte)'1'
};
[StructLayout(LayoutKind::Sequential)]
public ref struct ManagedStruct
{
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 11)]
String^ BrokerID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
String^ InvestorID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 31)]
String^ InstrumentID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
String^ OrderRef;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 16)]
String^ UserID;
EnumOrderPriceTypeType OrderPriceType;
EnumDirectionType Direction;
double LimitPrice;
};
Then I use StructureToPtr to copy the native object to managed object, and use WriteLine to test if the copy is successful,
NativeStruct *native = new NativeStruct();
ManagedStruct^ managed = gcnew ManagedStruct();
managed->LimitPrice = 95.5;
managed->BrokerID = "666666";
Marshal::StructureToPtr(managed, IntPtr(native), false);
int i;
for (i = 0; i < 11; i++)
Console::Write(native->BrokerID[i]);
Console::WriteLine();
Console::WriteLine(native->LimitPrice);
Console::WriteLine(L"Hello ");
Console::ReadLine();
My question is why LimitPrice is not copied successfuly? I have been battling this for a week, any help will be welcomed. Thanks a lot.
Marshal::StructureToPtr() can only work correctly when the managed and the native struct are an exact match. By far the simplest way to verify this is to check the sizes of the structures, they must be identical. So add this code to your program:
auto nlen = sizeof(NativeStruct);
auto mlen = Marshal::SizeOf(ManagedStruct::typeid);
System::Diagnostics::Debug::Assert(nlen == mlen);
Kaboom. The native struct takes 96 bytes and the managed one takes 104. Consequences are dire, you corrupt memory and that has a lot more unpleasant side effects than the LimitPrice member value getting copied to the wrong offset.
Two basic ways to trouble-shoot this. You can simply populate all of the managed struct members with unique values and check the first member of the native struct that has the wrong value. The member before it is wrong. Keep going until the you no longer get the kaboom. Or you can write code that uses offsetof() on the native struct members and compare them with Marshal::OffsetOf().
Just to save you the trouble, the problem are the enum declarations. Their size in the native struct is 1 byte but the managed versions take 4 bytes. Fix:
public enum struct EnumOrderPriceTypeType : Byte
and
public enum struct EnumDirectionType : Byte
Note the added : Byte to force the enum to take 1 byte of storage. It should be noted that copying the members one-by-one instead of using Marshal::StructureToPtr() is quicker and would have saved you a week of trouble.