Table of contents
Hi! π My name is Phill and I am Blockchain Tech Lead at The Fabricant and a Community Rep for Flow. You can find me on:
Discord: Phill#1854
Email: fullstackpho@protonmail.com
Github: github.com/ph0ph0
Twitter: twitter.com/fullstackpho
For a curated collection of the best Flow tools, blogs and articles, check out Get The Flow Down!
π¨βπ³ Introduction
One of the top priorities of the Flow Team when developing the Cadence language was developer experience (learn more here). They aimed to create an ergonomic programming language that was easy to learn and fun to use. Importantly, Cadence has been designed to be secure by default, which is of critical importance when writing smart contracts that may manage high-value assets!
Throughout my career as a blockchain engineer and developer in general, there are several tips and tricks that I have picked up along the way. I've found that these help me to be more efficient when writing smart contracts so that I can spend more time focusing on the logic and security of my code.
This article is aimed at those new to smart contract development on Flow. This is the first article in a two-part series, you can find the second article here. They contain an opinionated list of general tips and tricks that you may choose to incorporate into your smart contract development process. Indeed, many of these tips may fall under general software development best practices, but it is still worth reminding ourselves of their importance. Smart contract programming requires a different engineering mindset than you may be used to - the cost of failure can be high and change may be difficult, which in some ways makes it more similar to hardware programming than web or mobile development.
Although these tips and tricks facilitate the development process, they in no way detract from the quality of the code produced, which is why I am so keen to share these with you!
General Tips and Tricks
π Read Cadence Design Patterns, Anti-Patterns and Smart Contract Project Development Standards
When I first began writing smart contracts on Flow, I would read the Cadence Design Patterns and Cadence Anti-Patterns pages in the Official Cadence documentation before every contract that I wrote. Here are those articles:
developers.flow.com/cadence/design-patterns developers.flow.com/cadence/anti-patterns
Why did I read these articles before writing each smart contract? Knowing the best practices and anti-patterns for a particular language is key to writing solid code. Familiarity with documentation is akin to knowing what tools you have in your toolbox, but most importantly, knowing how to use them. Nowadays, I still read these articles every once in a while. Cadence is a continuously evolving language, with recent updates such as Secure Cadence, AuthAccount Capabilities and further updates on the horizon, including Stable Cadence. Reading these docs helps to remind me of the patterns and principles that constitute well-written contracts, as well as helping to keep fresh in my mind those that should be avoided.
Another good article to read is "Best Security Practices for Cadence Developers" by the famous blockchain security firm, Halborn.
It is also important that you read the Smart Contract Project Development Standards document. There's a lot of useful information in this doc that is not covered in this current article.
πΊοΈ Plan Before Starting
Once a contract is deployed, there are a limited set of updates that you can make to the deployed code.
The last thing you want as a blockchain engineer is to deploy code to production, only to realise that you have not included a functionality that was required by the client/business.
Planning (and testing) is critical to ensuring that all needs and responsibilities are fulfilled by the contract. There are many approaches that one can use for planning smart contracts, which I won't describe here, as that will be an article in itself. However, smart contract creation can usually follow any of the typical formats of software development planning used in other domains. Choose one and ensure that you complete the planning phase before implementing the contract itself!
π Keep It Simple
Ensure that your code is not overly complicated. Complexity increases the likelihood of errors. It is far better to have multiple, clear, succinct steps, than one obscure solution. You will thank yourself later if you ever have to revisit your code! This approach also facilitates auditing that your code might require.
For example, your NFT drop may involve two access paths for minting: users can mint if they are on a white list or if they own a particular NFT. You could certainly write a single mint function that would handle both of these cases, but should you? Would the mint function be easy to understand and test? Or could it inadvertently generate unseen edge cases and open up your code to vulnerabilities? A few more lines of code are much less of a worry than a potential security issue or improperly implemented logic.
To add to this, you should include comments in your code where necessary. This can help you and other developers quickly understand what your intentions were, and can also aid in identifying any unintended behaviours! Aim to keep your comments up-to-date as you expand the code base.
π§ͺ Never Leave Out Tests!
Always. Test. Thoroughly. This cannot be stressed enough. Room for testing should always be included in your time estimations.
The last thing you want is to deploy a smart contract to production, only to find out that the functionality does not work as expected or contains a vulnerability that can be exploited. The benefits of writing tests are huge.
Testing is a great exercise in truly understanding how your code works and how others might use your code maliciously. Every addition or change to the code must come with relevant and comprehensive tests.
Always add test cases to cover new attack vectors when they are discovered.
By testing comprehensively, you can move into launch phases with the confidence that if something goes wrong, it won't be the smart contract code!
π Break Up Your Contracts Into Sections Using Headers
As well as being easy to read, your contract should be easy to navigate. One trick you can use to achieve this is by including section headers in the contract itself. I achieve this by using comments that break up the code into sections. You should always aim to keep the order of sections consistent between contracts (this remains true even if you don't use section headers). Below is an example of the headers that I use in my smart contracts:
pub contract SmartContract {
// -------------------
// Paths
// -------------------
...Contract Paths such as storage paths and public paths etc...
// -------------------
// Contract Events
// -------------------
...Events that your contract emits...
// -------------------
// Contract State
// -------------------
...Contract-level variables and constants
// -------------------
// NFT Resource
// -------------------
...The NFT resource implementation in your contract...
// -------------------
// Collection Resource
// -------------------
...The Collection resource implementation in your contract...
// -------------------
// Admin Resource
// -------------------
...The Admin resource implementation in your contract...
// -------------------
// Private Utility Functions
// -------------------
...Functions that are only callable within your contract...
// -------------------
// Public Utility Functions
// -------------------
...Functions that are public such as some getters...
// -------------------
// Contract Init
// -------------------
... The init function of the contract...
}
This approach might not be to everyone's taste (some people prefer to put the contract initialisation at the top or might not even use section headers at all), however, I find one huge benefit in using this. When you are writing a smart contract, you will often have to jump between sections of your code. Without these headers, you will constantly be scrolling up and down trying to find resources, functions, events etc. By implementing headers in this manner, you can quickly jump to sections by simply entering 'find' mode in your IDE, and typing the start of the section that you would like to jump to - which makes navigating the code much quicker and development more seamless!
Conclusion
There are several small habits that blockchain engineers can utilise to make their life easier, and here I have documented a few. The overarching message here is that if you are familiar with best practices, common pitfalls and vulnerabilities, if you ensure your code is well documented, legible and structured, and if you strive to have comprehensive tests, then you're career as a blockchain engineer will surely benefit!
I hope you enjoyed this article, and please share your tips and tricks in the comments as I am always wanting to learn from you guys too! :)
Happy coding, and check out Part 2! π