I am writing a simple contract as a "backend" for a website of sample social media posts. From a frontend website using web3, I will let users add posts, and then when the webpage is loaded, I will ideally want to read all these posts saved in the contract (and display on the website). Everything (RegularPosts
) should be stored persistently.
This is my sample code, am I on the right track? Very new to Solidity -
pragma solidity >=0.7.0 <0.9.0;
contract Storage {
struct RegularPost {
uint256 category;
string name;
string post;
address addr;
uint256 date;
}
RegularPost[] public RegularPostArray;
function addNewPost(uint256 _category, string memory _name, string memory _post, uint256 _date) public {
RegularPostArray.push(RegularPost({category: _category, name: _name, post: _post, addr: msg.sender, date: _date}));
}
function getRegularPosts() public view returns (RegularPost[] memory) {
return RegularPostArray;
}
}
So a few questions:
Will this persistently store an array of posts as pushed to?
What is the storage limit on something like this?
Am I using the memory
keyword properly/as needed? Do I need to use the storage
keyword?
When getRegularPosts()
is called (via e.g. web3), what exactly will be returned? Will I (can I) get a JSON of everything? I'm not sure what I will receive.
Will this persistently store an array of posts as pushed to?
Yes. The contract doesn't have any code that would rewrite or delete the items, and the storage is persistent.
What is the storage limit on something like this?
A dynamic-size storage array can have up to 2^256
items, which is the limit of the index datatype (uint256
).
The actual storage slot location within the EVM storage is calculated using a hash of the index and few other params (see the docs), so there is a theoretical possibility of hash collisions, but it hasn't been proven in practice.
Am I using the
memory
keyword properly/as needed? Do I need to use thestorage
keyword?
I'm assuming you mean the RegularPost[] memory
as part of the getRegularPosts()
return statement (and not the string memory
in the addNewPost()
function params).
This is a correct use, because the return
statement can only use memory
or calldata
location. So the script loads the RegularPostArray
from storage to memory, and then returns it from the memory.
An example use of the storage
location is inside a function, when you're pointing to a storage variable:
function updatePost(uint256 _postIndex, uint256 _newCategory) external {
// any changes to the `post`
// will be stored in the `RegularPostArray[_postIndex]`
RegularPost storage post = RegularPostArray[_postIndex];
post.category = _newCategory;
}
But if you use a memory
location, Solidity works with the in-memory copy of the variable and doesn't update the storage:
function notUpdatePost(uint256 _postIndex, uint256 _newCategory) external {
// any changes to `post`
// will NOT affect `RegularPostArray[_postIndex]`
RegularPost memory post = RegularPostArray[_postIndex];
post.category = _newCategory;
}
When getRegularPosts() is called (via e.g. web3), what exactly will be returned? Will I (can I) get a JSON of everything? I'm not sure what I will receive.
It depends on the web3 library implementation (e.g. JavaScript library can return the data in a different structure compared to Python) and its version. But to be specific, in case of web3js@1.4.0
, it's an array of arrays, where each of the inner arrays represents the struct (with the values duplicated as both number-indexed and assoc-indexed):
[
[
'1',
'a',
'a',
'0x86beB187A30265Ce437C0BB55f38aF21554659Ae',
'1',
category: '1',
name: 'a',
post: 'a',
addr: '0x86beB187A30265Ce437C0BB55f38aF21554659Ae',
date: '1'
],
[
'2',
'b',
'b',
'0x86beB187A30265Ce437C0BB55f38aF21554659Ae',
'2',
category: '2',
name: 'b',
post: 'b',
addr: '0x86beB187A30265Ce437C0BB55f38aF21554659Ae',
date: '2'
]
]