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;
}
Related
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.
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
)
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;
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.
bellow code is the marshling of a native win32 code.
but i get an error message
type load exception, can not load from assembly because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field
there is a structure S1 with both value-type member and reference-type.. this structure is a member of union which has to have fieldOffset, but all S1 members can not start from fieldOffset 0 they are a mixture of reference and value type...how can I handle that??
[StructLayout(LayoutKind.Sequential)]
public struct S1
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Const.FieldSizeMsgid + 1)]//Reference Type
public String MsgId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Const.FieldSizeTime + 1)]//Reference Type
public String SendTime;
public UInt32 SubsSeq;//Value Type
public UInt32 ServTime;//Value Type
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = Const.FieldSizeFillerA1 + 1)]//Reference Type
public String Filler;
}
[StructLayout(LayoutKind.Explicit)]
public struct AdminData
{
[FieldOffset(0)] public S1 S11;// get an error because the S1 has both reference type member and value type member
[FieldOffset(0)] public S2 S22;
[FieldOffset(0)] public S3 S33;
}
I know I have to break the S1 into 2 structures, one with value-type members and the other for reference-type members..but I do not know how to do it and how to reference them in AdminData which is a union.
EDIT:
here is the c++ code
typedef struct S1
{
char MsgId [Const.FieldSizeMsgid + 1];//Reference Type
char SendTime[Const.FieldSizeTime + 1];//Reference Type
int SubsSeq;//Value Type
int ServTime;//Value Type
char Filler[Const.FieldSizeFillerA1 + 1];//Reference Type
}
union AdminData
{
S1 S11;//has both value type member and reference type member
S2 S22;//has both value type member and reference type member
S3 S33;//has both value type member and reference type member
}
typedef struct MMTPMsg
{
int Length;
short Type;
AdminData Data; //the union
long long TimeStamp;
}
As you have discovered you cannot overlay reference types on top of value types. So to implement your union, you need to use either one or the other. Your structures must contain value types and so we conclude that you must use value types exclusively.
So, how do you implement your character arrays as value types? By using a fixed size buffer.
unsafe public struct S1
{
fixed byte MsgId[Const.FieldSizeTime + 1];
....
}