Suppose I have this data structure layout:
struct ReviewStruct {
string rating;
...
}
struct Restaurant {
...
uint reviewCount;
mapping(uint => ReviewStruct) reviews;
}
uint public restaurantCount = 0;
mapping(uint => Restaurant) public restaurants;
Then, when I'm trying to access stuff in my JS app, it works, but not if I'm trying to access an actual review:
const restaurantCount = await review.methods.restaurantCount().call() // works
const restaurant = await review.methods.restaurants(2).call() // works
const reviewObj = await review.methods.restaurants(2).reviews(0).call() // throws an error
How do I access a mapping that is inside of a mapping (both are related to structs)?
On old ABI v1 encoding, structs cannot be returned by public functions, including automatically generated one for your mapping. This is simply not possible. You need to create your own accessor function that returns field values as a tuple (list of value) or use a toolchain that supports ABI v2 encoding.
Furthermore I am not sure if accessor functions are automatically generated for mappings of mappings, so you might end up writing your own function any case.
Related
Is it possible to write a TypeScript Function which programmatically creates copies of an object, without hardcoding references to the object properties being copied?
I'm trying to enable a user workflow where the user is empowered to create copies of a specified object, i.e. create and partially complete one object using a Form, create 10x duplicates of the new object, fill-in data for new objects.
I've succeeded in creating a Function-Backed Action which duplicates a designated object, but all references to the properties being copied are hardcoded, which is less than ideal for maintenance and is a relatively common request for our org.
Code example for desired Function:
#OntologyEditFunction()
public GenerticBatchCopyObjects(mySelectedObject: ObjectAPIName, numberNewObjects: Integer): void {
/** Set UUID primary key */
let object_pk = Uuid.random()
for (let i = 1; i <= numberNewObjects; i++) {
/** initialize new object */
const newObject = Objects.create().ObjectAPIName(object_pk + "_" + String(i))
/** copy all properties from selected object record to new object */
for property in mySelectedObject.properties:
if property != "name_of_primary_key_column":
newObject.property = mySelectedObject.property
}
}
There’s not really a nice way to achieve this currently and it’s maybe not advised. This is primarily because the list of properties that will be copied from an object are fixed at publish-/compile-time in any (nice) method I can see.
Partial type-safe solution for copying properties only
I've included the most generic version of this function I can construct below, but it has some limitations. More concretely, the copy function
does not copy links that aren't represented by FK properties (i.e. it only copies properties);
does not adapt and might break when there are new or removed properties; and
is not easily maintained as functionality can change depending on when it is compiled.
private isPrimaryKey(x: string): x is MyObjectType["primaryKey"]["apiName"] {
return x === MyObjectType.primaryKey.apiName;
}
#OntologyEditFunction()
public copy(obj: MyObjectType): void {
const new_obj = Objects.create().myObjectType(Uuid.random());
var prop : keyof typeof MyObjectType.properties;
for (prop in MyObjectType.properties) {
if (!this.isPrimaryKey(prop)) {
new_obj[prop] = obj[prop];
}
}
}
Explicit type-safe solution for copying everything
The approach below requires more manual adjustment if the ontology changes, but makes it explicit that the code must be changed in line with ontology changes. This approach also copies links.
It is also worth noting that this behaviour may not be desired behaviour because it doesn't recursively copy the linked objects and instead attempts to copy the links. Please test that this gives the desired results.
static PROPERTIES = ["myProperty"] as const;
static MULTILINKS = ["myMultiLink"] as const;
static SINGLELINKS = ["mySingleLink", "myOtherSingleLink"] as const;
#OntologyEditFunction()
public copy2(obj: MyObjectType): void {
const new_obj = Objects.create().myObjectType(Uuid.random());
MyFunctions.PROPERTIES.forEach(p => {
new_obj[p] = obj[p];
});
MyFunctions.MULTILINKS.forEach(p => {
obj[p].all().forEach(v => new_obj[p].add(v));
});
MyFunctions.SINGLELINKS.forEach(p => {
const v = obj[p].get();
if (v !== undefined) {
new_obj[p].set(v);
}
});
}
You may need to exclude some of the code if your object type does not have properties/multi-links/single-links to make the compiler happy.
I am currently learning Solidity language and I've noticed that when I'm trying to get the value of a Struct inside my JS code, Solidity returns every variable without arrays.
I have to create custom getter to access all the data inside my struct.
I've made a very simple example of a contract with a Struct initialized inside the constructor.
I'm accessing the variable with my custom getter and generated one inside JS code.
Test.sol
pragma solidity ^0.8.4;
contract Test {
struct Data {
string foo;
address[] bar;
address ctrt;
}
Data public d;
constructor() {
d.foo = "HELLO WORLD";
d.bar.push(msg.sender);
d.ctrt = address(this);
}
function getD() public view returns (Data memory) {
return d;
}
}
Test.js
const {ethers} = require('hardhat');
describe('Test', function () {
it('should test something', async function() {
const factory = await ethers.getContractFactory('Test')
const test = await factory.deploy();
console.log("Result from var:");
console.log(await test.d());
console.log("Result from getter:");
console.log(await test.getD());
})
});
Result in console:
Result from var:
[
'HELLO WORLD',
'0x5FbDB2315678afecb367f032d93F642f64180aa3',
foo: 'HELLO WORLD',
ctrt: '0x5FbDB2315678afecb367f032d93F642f64180aa3'
]
Result from getter:
[
'HELLO WORLD',
[ '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ],
'0x5FbDB2315678afecb367f032d93F642f64180aa3',
foo: 'HELLO WORLD',
bar: [ '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' ],
ctrt: '0x5FbDB2315678afecb367f032d93F642f64180aa3'
]
What is the point to explicitly say that a variable is public if some part of the data is not visible?
The automatic getters that are made for structs return all members in the struct except for members that are mappings and arrays.
The Solidity docs explain:
The mapping and arrays (with the exception of byte arrays) in the struct are omitted because there is no good way to select individual struct members or provide a key for the mapping
So for your example the auto-generated getter looks like this:
function d() public returns (string foo, address ctrt) {
foo = d.foo;
ctrt = d.ctrt;
}
And as expected, in your example the auto-generated getter returns all struct members that are not mappings or arrays, and your getter returns all members.
Quoting docs:
If you have a public state variable of array type, then you can only retrieve single elements of the array via the generated getter function. This mechanism exists to avoid high gas costs when returning an entire array.
That's why when you make the call using ethers.js you don't see that value.
Solution is to create a getDataBar(uint i) function that returns bar contents.
When I try to exceute a solidity smart conract function to store 32 input parameters, I am getting a stack too deep error.
To resolve that, at the middleware we have consolidated and created an array of bytes32 for 24 input parameters and the rest of the 8 parameters are of string datatype. But again we get the same stack too deep error.
How can I possibly exceute a function to store the 32 input parameters?
We are making use of solidity compiler version 0.4.25.
This is the basic skeletal of code
struct Sample {
bytes32 key;
string str1;
.
.
.
.
.
string str8;
byts32[] someArray; //23 elements
}
mapping(bytes32 => Sample) sampleMap;
function set(bytes32 key,string str1,string str2,......,string
str8,bytes32[] array) public returns(bool) {
//set the values using sampleMap mapping;
return true;
}
function get(bytes32 key) returns(bytes32 key,string str1,string
str2,......,string str8,bytes32[] array) {
//retrieve values from struct using sampleMap mapping
}
I need to store a mapping between a string to items which are 128 bytes long in a solidity contract. The problem is that the longest bytes data type is bytes32, which is not long enough, and if I try to store my strings in a string array I get the following error:
This type is only supported in the experimental ABI encoder. Use "pragma experimental abiencoderv2;" to enable this feature
So I cannot use bytes32 because it's not big enough. I cannot use bytes because it's not supported. And I cannot use string[] because it's experimental and not recommended in production.
Any solution?
This is the contract I'm using:
pragma solidity ^0.4.24;
contract SomeData {
struct Data {
string id;
string[3] items;
}
mapping (string => Data) dataItems;
function addData(string id, string[3] items) public {
Data memory data = Data(id, items);
data.id = id;
data.items = items;
dataItems[id] = data;
}
function getDataItems(string id) public view returns (string[3]){
return dataItems[id].items;
}
}
Since you know the maximum length of each individual string, you can use a static 2 dimensional array:
contract SomeData {
struct Data {
string id;
byte[128][100] items;
}
mapping (string => Data) dataItems;
function addData(string id, byte[128][100] items) public {
Data memory data = Data(id, items);
data.id = id;
data.items = items;
dataItems[id] = data;
}
function getDataItems(string id) public view returns (byte[128][100]){
return (dataItems[id].items);
}
}
Note, only 2-dimensional dynamic arrays are not allowed. You could even use byte[128][] if you wanted to.
I am trying to create a REST service using Nancy FX in a C# environment. I can easily do a Response.AsJson and it all looks good. But I want the response to omit any properties that are null.
I have not been able to figure out how to do this yet.
Could someone point to me towards a help document or a blog post somewhere, that explains how to do this.
Thanks,
JP
I would create a dynamic anonymous type and return that. So let's say you have a User object like this:
public class User
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
You want to pass back an instance of this type as a JSON response so you will have some code like this:
Get["/user/{userid}"] = parameters =>
{
var user = UserService.GetById(Db, (string)parameters.userid);
if (user == null) return HttpStatusCode.UnprocessableEntity;
return Response.AsJson(user);
};
But you don't want to return the User instance, instead you want to return an separate instance of a dynamic type that will only implement a property if the property value is not null for a given instance.
So I would suggest code something like this:
Get["/user/{userid}"] = parameters =>
{
var user = UserService.GetById(Db, (string)parameters.userid);
if (user == null) return HttpStatusCode.UnprocessableEntity;
dynamic userDTO = new ExpandoObject();
userDTO.Id = user.Id;
if (!string.IsNullOrEmpty(user.FirstName)) userDTO.FirstName = user.FirstName;
if (!string.IsNullOrEmpty(user.LastName)) userDTO.Lastname = user.LastName;
return Response.AsJson((ExpandoObject)userDTO);
};
Note 1
You don't need to test for the Id since that is implied by the successful return of the User instance from the database.
Note 2
You need to use a dynamic type so you can include ad hoc properties. The problem is that extension methods cannot accept dynamic types. To avoid this you need to declare it as an ExpandoObject but use it as a dynamic. This trick incurs a processing overhead but it allows you to cast the dynamic to an ExpandoObject when passing it in to the AsJson() extension method.