Solidity: problem creating a struct containing mappings inside a mapping - struct

This is my code where i am trying to create a struct containing two mappings and insert the structs into a mapping:
pragma solidity ^0.7.2;
contract Campaign {
struct Usuario {
string id;
mapping(string => uint) debe;
mapping(string => uint) leDebe;
}
Usuario[] public usuarios;
uint numUsuarios;
mapping(string => Usuario) public circulo;
constructor () {
}
function usuarioPrueba(string memory id, string memory idDebe, uint valDebe, string memory idLeDebe, uint valLedebe) public {
usuarios.push();
Usuario storage newUsuario = usuarios[numUsuarios];
numUsuarios++;
newUsuario.id = id;
newUsuario.debe[idDebe] = valDebe;
newUsuario.leDebe[idLeDebe] = valLedebe;
circulo[id] = newUsuario;
}
}
but I am getting the following error at line 28 (circulo[id] = newUsuario;) on Remix:
TypeError: Types in storage containing (nested) mappings cannot be
assigned to. circulo[id] = newUsuario;
Thank you so much for the help beforehand and I am sorry for my english, I am from Spain and if the solution its just to obvious, I am kind of new to solidity and smart contracts.

Since v 0.7.0 you cannot assign structs containing nested mappings. What you can do instead is to create new instances like this one and then asign the values to the properties of the struct!
Usuario storage newUsuario = circulo[id];
numUsuarios++;
newUsuario.id = id;
newUsuario.debe[idDebe] = valDebe;
newUsuario.leDebe[idLeDebe] = valLedebe;

Related

Solidity struct mapping not stored in contract

I read many articles on how to use mappings, mappings in struct and came out with something that should be correct to me, based on a few threads.
I know that since solidity 0.7.0 things have changed with nested mappings in struct and did the following :
contract Test {
constructor() {
}
struct Bid {
uint auction_id;
address addr;
uint amount;
}
struct Auction {
uint id;
string dtype;
uint start_date;
uint end_date;
string label;
uint price;
uint amount;
bool closed;
mapping(uint => Bid) bids;
uint bidCount;
}
uint public auctionCount = 0;
mapping(uint => Auction) public auctions;
function createAuction( string memory plabel, string memory ptype, uint nbhours, uint pprice) external {
Auction storage nd = auctions[auctionCount];
nd.id = auctionCount;
nd.dtype = ptype;
nd.start_date = block.timestamp;
nd.end_date = block.timestamp+nbhours*60*60;
nd.label = plabel;
nd.price = pprice;
nd.amount = 0;
nd.closed = false;
nd.bidCount = 0;
auctionCount++;
}
}
Everything compiles fine, the createAuction transaction is succesful.
When checking on the contract in Ganache, auctionCount is incremented but I have no items added in the drawsmapping.
I also debugged the transaction with truffle and it goes through the function, assigning values through the execution of createAuction, but the changes are not persistent.
I even tried removing one string attribute since I read that when there are 3 it could have been a problem (ok, I have only 2 max ;)).
I must have missed something, but I'm out of options right now.
Thanks in advance for your help !
If you are talking about auctions mapping, ensure you use the correct index when accessing mapping items. In your case, the first Auction item you add to the mapping will have a 0 index. I tried your contract in Remix, and everything worked well.

Solidity Error: Version 0.8.0 : Struct containing a (nested) mapping cannot be constructed

struct Campaign {
address payable campaignOwner;
string campaignTitle;
string campaignDescription;
uint256 goalAmount;
uint256 totalAmountFunded;
uint256 deadline;
bool goalAchieved;
bool isCampaignOpen;
bool isExists;
mapping(address => uint256) contributions;
}
//stores a Campaign struct for each unique campaign ID.
mapping(uint256 => Campaign) campaigns;
function createCampaign(string memory _campaignTitle, string memory _campaignDescription, uint256 _goalAmount, uint256 _fundingPeriodInDays ) public {
++totalCampaigns;
uint256 period = block.timestamp + (_fundingPeriodInDays * 1 days);
Campaign memory aCampaign = Campaign(payable(msg.sender),_campaignTitle, _campaignDescription, _goalAmount, 0, period , false, true, true);
campaigns[totalCampaigns] = aCampaign;
}
I am using Solc version 0.8.0. When I try to create a Struct that contains mapping, I received an error:
Struct containing a (nested) mapping cannot be constructed.
When I use older versions of solc (0.5.8), the code compiles without problems. But this version is not supported with other solidity files and giving error as:
Source file requires different compiler version (current compiler is
0.8.13+commit.abaa5c0e.Emscripten.clang) - note that nightly builds are considered to be strictly less than the released version
You can not keep a struct containing a mapping in memory, and you can not initialize a storage object in a function, it must be a state variable. So what you can do is, get an object from your mapping, assign it to a local variable and modify it. Like this:
function createCampaign(string memory _campaignTitle, string memory _campaignDescription, uint256 _goalAmount, uint256 _fundingPeriodInDays ) public {
++totalCampaigns;
uint256 period = block.timestamp + (_fundingPeriodInDays * 1 days);
Campaign storage aCampaign = campaigns[totalCampaigns];
aCampaign.campaignOwner = payable(msg.sender);
aCampaign.campaignTitle = _campaignTitle;
aCampaign.campaignDescription = _campaignDescription;
aCampaign.goalAmount = _goalAmount;
aCampaign.totalAmountFunded = 0;
aCampaign.deadline = period;
aCampaign.goalAchieved = false;
aCampaign.isCampaignOpen = true;
aCampaign.isExists = true;
}

Is the mapping in solidity of one key on multiple structs of same type possible?

I am trying to map one address on multiple structs of same type, which belongs to the same address. How can I do this, if I want to choose any of the "stored" structs for one address on request afterwards?
I created a struct called Prescription, and a mapping with the patients address. So what I really want is to map the patients address to several Prescription-structs.
struct Prescription {
address patients_address;
string medicament;
string dosage_form;
uint amount;
uint date;
}
mapping (address => Prescription) ownerOfPrescription;
address [] public patients;
function createPrescription(address patients_address, string medicament, string dosage_form, uint amount, uint date) public restricted {
var newPrescription = ownerOfPrescription[patients_address];
newPrescription.medicament = medicament;
newPrescription.dosage_form = dosage_form;
newPrescription.amount = amount;
newPrescription.date = date;
patients.push(patients_address) -1;
}
function getPre(address _address)view public returns (string, string, uint, uint){
return(
ownerOfPrescription[_address].medicament,
ownerOfPrescription[_address].dosage_form,
ownerOfPrescription[_address].amount,
ownerOfPrescription[_address].date);
}
Now I would have a function, where I can call all written Prescriptions for one patient. Actually I am able to call only the last written prescription for one address.
Sure, the value type of a mapping can be an array:
// map to an array
mapping (address => Prescription[]) ownerOfPrescription;
function createPrescription(...) ... {
// add to the end of the array
ownerOfPrescription[patients_address].push(Prescription({
medicament: medicament,
...
});
patients.push(patients_address);
}

Access String value in enum without using rawValue

I would like to replace my global string constants with a nested enum for the keys I'm using to access columns in a database.
The structure is as follows:
enum DatabaseKeys {
enum User: String {
case Table = "User"
case Username = "username"
...
}
...
}
Each table in the database is an inner enum, with the name of the table being the enum's title. The first case in each enum will be the name of the table, and the following cases are the columns in its table.
To use this, it's pretty simple:
myUser[DatabaseKeys.User.Username.rawValue] = "Johnny"
But I will be using these enums a lot. Having to append .rawValue to every instance will be a pain, and it's not as readable as I'd like it to be. How can I access the String value without having to use rawValue? It'd be great if I can do this:
myUser[DatabaseKeys.User.Username] = "Johnny"
Note that I'm using Swift 2. If there's an even better way to accomplish this I'd love to hear it!
While I didn't find a way to do this using the desired syntax with enums, this is possible using structs.
struct DatabaseKeys {
struct User {
static let identifier = "User"
static let Username = "username"
}
}
To use:
myUser[DatabaseKeys.User.Username] = "Johnny"
Apple uses structs like this for storyboard and row type identifiers in the WatchKit templates.
You can use CustomStringConvertible protocol for this.
From documentation,
String(instance) will work for an instance of any type, returning its
description if the instance happens to be CustomStringConvertible.
Using CustomStringConvertible as a generic constraint, or accessing a
conforming type's description directly, is therefore discouraged.
So, if you conform to this protocol and return your rawValue through the description method, you will be able to use String(Table.User) to get the value.
enum User: String, CustomStringConvertible {
case Table = "User"
case Username = "username"
var description: String {
return self.rawValue
}
}
var myUser = [String: String]()
myUser[String(DatabaseKeys.User.Username)] = "Johnny"
print(myUser) // ["username": "Johnny"]
You can use callAsFunction (New in Swift 5.2) on your enum that conforms to String.
enum KeychainKey: String {
case userId
case email
}
func callAsFunction() -> String {
return self.rawValue
}
usage:
KeychainKey.userId()
You can do this with custom class:
enum Names: String {
case something, thing
}
class CustomData {
subscript(key: Names) -> Any? {
get {
return self.customData[key.rawValue]
}
set(newValue) {
self.customData[key.rawValue] = newValue
}
}
private var customData = [String: Any]()
}
...
let cData = CustomData()
cData[Names.thing] = 56
Edit:
I found an another solution, that working with Swift 3:
enum CustomKey: String {
case one, two, three
}
extension Dictionary where Key: ExpressibleByStringLiteral {
subscript(key: CustomKey) -> Value? {
get {
return self[key.rawValue as! Key]
}
set {
self[key.rawValue as! Key] = newValue
}
}
}
var dict: [String: Any] = [:]
dict[CustomKey.one] = 1
dict["two"] = true
dict[.three] = 3
print(dict["one"]!)
print(dict[CustomKey.two]!)
print(dict[.three]!)
If you are able to use User as dictionary key instead of String (User is Hashable by default) it would be a solution.
If not you should use yours with a nested struct and static variables/constants.

How to create a method at runtime using Reflection.emit

I'm creating an object at runtime using reflection emit. I successfully created the fields, properties and get set methods.
Now I want to add a method. For the sake of simplicity let's say the method just returns a random number. How do I define the method body?
EDIT:
Yes, I've been looking at the msdn documentation along with other references and I'm starting to get my head wrapped around this stuff.
I see how the example above is adding and/or multplying, but what if my method is doing other stuff. How do I define that "stuff"
Suppose I was generating the class below dynamically, how would I create the body of GetDetails() method?
class TestClass
{
public string Name { get; set; }
public int Size { get; set; }
public TestClass()
{
}
public TestClass(string Name, int Size)
{
this.Name = Name;
this.Size = Size;
}
public string GetDetails()
{
string Details = "Name = " + this.Name + ", Size = " + this.Size.ToString();
return Details;
}
}
You use a MethodBuilder to define methods. To define the method body, you call GetILGenerator() to get an ILGenerator, and then call the Emit methods to emit individual IL instructions. There is an example on the MSDN documentation for MethodBuilder, and you can find other examples of how to use reflection emit on the Using Reflection Emit page:
public static void AddMethodDynamically(TypeBuilder myTypeBld,
string mthdName,
Type[] mthdParams,
Type returnType,
string mthdAction)
{
MethodBuilder myMthdBld = myTypeBld.DefineMethod(
mthdName,
MethodAttributes.Public |
MethodAttributes.Static,
returnType,
mthdParams);
ILGenerator ILout = myMthdBld.GetILGenerator();
int numParams = mthdParams.Length;
for (byte x = 0; x < numParams; x++)
{
ILout.Emit(OpCodes.Ldarg_S, x);
}
if (numParams > 1)
{
for (int y = 0; y < (numParams - 1); y++)
{
switch (mthdAction)
{
case "A": ILout.Emit(OpCodes.Add);
break;
case "M": ILout.Emit(OpCodes.Mul);
break;
default: ILout.Emit(OpCodes.Add);
break;
}
}
}
ILout.Emit(OpCodes.Ret);
}
It sounds like you're looking for resources on writing MSIL. One important resource is the OpCodes class, which has a member for every IL instruction. The documentation describes how each instruction works. Another important resource is either Ildasm or Reflector. These will let you see the IL for compiled code, which will help you understand what IL you want to write. Running your GetDetailsMethod through Reflector and setting the language to IL yields:
.method public hidebysig instance string GetDetails() cil managed
{
.maxstack 4
.locals init (
[0] string Details,
[1] string CS$1$0000,
[2] int32 CS$0$0001)
L_0000: nop
L_0001: ldstr "Name = "
L_0006: ldarg.0
L_0007: call instance string ConsoleApplication1.TestClass::get_Name()
L_000c: ldstr ", Size = "
L_0011: ldarg.0
L_0012: call instance int32 ConsoleApplication1.TestClass::get_Size()
L_0017: stloc.2
L_0018: ldloca.s CS$0$0001
L_001a: call instance string [mscorlib]System.Int32::ToString()
L_001f: call string [mscorlib]System.String::Concat(string, string, string, string)
L_0024: stloc.0
L_0025: ldloc.0
L_0026: stloc.1
L_0027: br.s L_0029
L_0029: ldloc.1
L_002a: ret
}
To generate a method like that dynamically, you will need to call ILGenerator.Emit for each instruction:
ilGen.Emit(OpCodes.Nop);
ilGen.Emit(OpCodes.Ldstr, "Name = ");
ilGen.Emit(OpCodes.Ldarg_0);
ilGen.Emit(OpCodes.Call, nameProperty.GetGetMethod());
// etc..
You may also want to look for introductions to MSIL, such as this one: Introduction to IL Assembly Language.

Resources