Tips for Creating Smart Contracts 1/2
This article aims to give an overview of tips and techniques for saving gas cost when creating a Smart Contract. The article is divided into two parts. The first part focuses on some general guidelines and technique on how to create a Smart Contract. The second part tries to use these guidelines and methods to optimize an example contract. We are going to compare different versions of that contract and try to find out which is the cheapest and what are some advantages and disadvantages of some of the variants. If you need a reminder what Smart Contracts are and what their advantages and disadvantages are, feel free to visit the following link (Introduction to Smart Contracts, https://ethereum.org/en/developers/docs/smart-contracts/).
Use Case Example Contract
For the Blockchain Academy Mittweida we want to provide a possibility for the users to submit feedback for the available courses. To provide an anonymous way for the user to submit his feedback but still be able to detect if one user submitted multiple feedbacks for a course, we use a technology called ring signatures. This technology groups users together and enables them to sign messages in the name of the group. It is possible to verify if a message was submitted out of that group, but not who send it. We want to group the public keys of all users who are enrolled in a course and provide a decentralized way to save the information. For that purpose, we use a Smart Contract that can hold the public keys per course and offers a way to access this information.
Part One – Guidelines and techniques for Smart Contracts
General Philosophy
- The more complex the contract gets, the probability for errors rises. Your code should be responding to bugs gracefully. The contract should have a build in fail-safe, the amount of money at risk should be limited, and there needs to be an effective upgrade path for your contract.
- Smart Contracts are fairly new, so discoveries of new vulnerabilities, bugs, updates and best practices are constantly evolving, you should keep up to date to the newest developments and adjust your Smart Contracts accordingly.
- Smart Contracts should be kept as short and simple as possible. Every Operation the Smart Contract is able to do will cost gas once we deploy the contract.
- Test your code thoroughly, once deployed it is difficult to change your Smart Contract.
Source: https://consensys.github.io/smart-contract-best-practices/general-philosophy
Development Advises
- External Calls should be kept to a minimum, as they introduce the risk for malicious code execution, especially when calling unknown or untrusted contracts.
- Every Data that is saved in a Smart Contract is publicly available, keep that in mind when working with sensitive data.
- To use 256 bit variables, each storage slot of the Ethereum Virtual Machine (EVM) has 256 bit. When you want to store an 8 bit variable like uint8 for example the remaining digits of that storage slot have to be filled with zeroes this is an operation which costs gas.
- Variables can be packed in a single storage slot, when using a struct for example every variable in it can be adjusted in size, so they fit in a 256 Bit slot.
In this example the command for storage SSTORE, which is the most expensive command with 20,000 gas, would only be needed twice. Once for storing a, b and c and once for storing d. - Storing values in the bytecode of the contract is a relatively cheap way of storing Information, the downside is these values cannot be changed. The values need to be hardcoded or the keyword constant needs to be used when declaring a variable.
Adding the constant to the variables v2 and v3 reduces the cost of the contract from 0.00054263 to 0.00043118 Eth. - Use local variables in loops, for example you loop through an array and want to increase a counter every time, declare a local variable in that function and then write the result to the “real” variable, otherwise every read and write operation to that variable is done on the blockchain which is quite expensive.
Sources:
https://medium.com/coinmonks/8-ways-of-reducing-the-gas-consumption-of-your-smart-contracts-9a506b339c0a
https://mudit.blog/solidity-gas-optimization-tips/
Events
The main Goal for our Smart Contract is to provide a way to save the public keys of a user group. Saving large amounts of data in the Smart Contract is generally not recommended due to the gas cost. Possible solutions for this are to use an external Database that just saves the address in the Smart Contract or use the logging capabilities of Smart Contracts and the blocks they are written to save information via the Event functionalities. Events store arguments that are passed to them in transaction logs, these logs are stored on blockchain and are accessible using the address of the contract as long as the contract is present on the blockchain. The Downside for this technique is that these transaction logs are not accessible for any Smart Contract, but they can be read and subscribed to with for example JavaScript function and Frameworks. The Advantage of storing information this way is that it is considerably cheaper than storing the same information in an array, for example.
Factory Pattern
For our example, it could be possible to deploy for every course on its own Smart Contract. The contract could possibly be made simpler because we just have to save the data for one course instead of an unknown number of courses. We just need a Blueprint Contract and a Contract or function that deploys a new Smart Contract every time a new course is created. This is called a Factory Pattern in programming. In solidity (programming language of Ethereum Smart Contracts) we have two types of this factory patter. First, a standard Factory and second, a Clone Factory. The standard Factory contains the information of the blueprint Smart Contract and gets deployed, after that it can deploy new copies of the Blueprint Smart Contract with a function call and if necessary, can pass arguments to it if needed. This provides an easy way to remotely deploy new Smart Contracts if needed, but it does not save gas cost. We still have to pay the full cost for deploying the contract. An alternative to this is to use a Clone Factory or minimal Proxy. With this we deploy a so-called Logic Contract, a Smart Contract that contains the functionality and a Proxy or Clone Factory that knows the address of the Logic Contract. Now we can create proxies that have minimal functionality to forward our function call with a delegate to our Logic Contract. This approach reduces the cost for the deployment of the clones considerably.