Aren’t mappings great?
At Modular, we’re busy working on building a great set of standard libraries that are easy to use, well documented, well tested, and most importantly, secure. We want to help make Ethereum and Solidity safer and more accessible for anyone doing development, whether they are a complete newbie or a seasoned pro. A couple months ago, I realized that a linked list would be useful for a few different applications, most importantly the implementation of the Interactive Token Sale that we are close to finishing building. So I did some research!
Never Start From Scratch unless you need to
I could have started from scratch but why do that when you can take advantage of the shared knowledge and work of all the other amazing developers in the community! That is something that everyone needs to take to heart here. Odds are, if there is something you want to do, someone has at least started working on it, and there is a lot you can learn from engaging with the community. We found
’s repo here and thought it was a great place to start.
From Wikipedia: In computer science, a linked list is a linear collection of data elements, in which linear order is not given by their physical placement in memory. Instead, each element points to the next.
A circular doubly linked list is when two consecutive elements are linked by previous and next pointer and the last node points to first node by next pointer and also the previous pointer of the head node points to the tail node.
Check out the full library here!
The Linked List library provides functionality needed to create and manipulate a uint256-indexed Circular Linked List Data structure by allowing a multitude of different functions to be used to interact with the struct. Functions like push() and pop() can be used to create a FILO stack or FIFO ring buffer. getAdjacent() can also be used to iterate over the list. Here is a brief description of the functionality:
- Can check if the list exists and find the size.
- Can check if a certain node exists.
- Gets adjacent nodes to a specified node.
- Finds a spot in a sorted list for a new node to be placed.
- Insert nodes and create links between nodes.
- Remove, push, and pop nodes from the list.
LinkedList is a nested mapping with the first key being the node index (uint256) and the second being the bidirectional link (bool) to a neighboring node. Key 0 implies the head so writes to LinkedList.list[0] or manually linking to Linkedlist.list[0] (e.g. list[var1][false] = 0;) are to be avoided by the calling contract.
Design Decisions
Linked lists are powerful data structures and can be implemented in a variety of ways. I saw what
did in his implementation and I knew that was the best place to start.
Using a mapping:
Using the mapping to store the list made the most sense because of the efficiency benefits. Checking if a node exists in the linked list requires only a few lookups because you can instantly check to see if that entry in the mapping has a valid PREV and NEXT pointer. No iterating through the list is required, essentially making this a O(1) operation.
One drawback of using a mapping is that there can only be one entry per number. This could cause some potential problems in the future, but can be circumvented by having another mapping that indicates how many entries there are at a certain index:
Another drawback of the mapping is that it limits the amount of information that can be stored in the first order data struct. I think this is a necessary tradeoff for usability because the goal of this is to make it useable by anyone. Keeping the list struct as simple as possible accomplishes that. If someone wants to store other information about the node, they can use a separate mapping that is indexed by the nodes in the linked list, like the numEntries case explained above.
Indexing with uint256:
If you think about it, these could be indexed with any data type, but I chose uint256. I think this is the best choice because it allows for linked lists sorted smallest to largest. This again allows for easy lookups and inserts, because when searching for a sorted spot, you can use knowledge about what is already in the list to expedite the search, saving gas.
As you can see, you can make a prediction about where the node to be inserted will go, potentially speeding up the search. Obviously, the search is still O(n), but can potentially be sped up.
Descriptive Function Returns:
You’ll see that some of the functions return multiple values, some including booleans. That is because I want the contracts to be as easy to use and understand as possible. I only want state to get modified if arguments are sent correctly. This is something that we believe is a good design practice for smart contracts in general.
Unless explicitly needed, always do whatever you can to ensure that the sender of function calls is sending all the correct arguments, even if it is the owner of the contract.
People make mistakes all the time and it is important to help avoid those causing issues.
Using the Library in Your Contract
You can find examples in the repo about how to use the Linked List library, but I will give a quick rundown of how to use it.
- Copy the LinkedListLib.sol into your contracts folder.
- Include these lines at the beginning of your contract:
3. Thats it! Now you can call any of the functions in the library by using the dot operator.
We have more detailed instructions in our repo for this and all our other libraries! Go check us out!
Conclusion
Again, here is the link to our library that you can use.
I hope you all enjoyed my quick foray into Solidity linked lists! We’re always looking for ways to improve, so any suggestions about the implementation to help with security, usability, efficiency, or documentation are greatly encouraged and appreciated!
We care a lot about making Ethereum secure and useable, so check out our website to see what we do!
Thanks to
for help on the article and library.