Avoiding common mistakes
Because of how different blockchain programming is from other types of programming, new blockchain developers often make certain mistakes. This section covers some functional and security problems that developers should know about working with Tezos and other blockchains:
Keeping contracts from becoming blocked
Because the code of a contract cannot be changed after it is deployed, it's important to test contracts thoroughly before deploying them. This includes testing all points in its life cycle, including its initial deployment and storage, its behavior throughout its lifespan, and how it is removed from use.
One simple mistake that happens surprisingly often is not providing a way for an administrator to withdraw tez that are in the contract. Without an explicit way of making the contact send tokens to another address, any tez intentionally or mistakenly sent to the contract are locked in it forever.
Here are some other ways that contracts can become blocked and unusable:
Remember that a contract can become unusable because of mistakes by the developer, sometimes exploited by malicious activities by bad actors.
-
Storage becomes too large: As described in Staying within resource constraints, a contract's storage can become too large, making it impossible to load the contract and its storage in a single transaction. When this happens, it becomes impossible to use the contract, even to call entrypoints to reduce its storage. For this reason, ensure that the size of the contract's storage is limited, such as using big-maps for data instead of standard maps.
-
Computation requirements become too large: Similarly, there is a limit to the amount of computation that can happen in a single transaction. If the contract contains loops or other iterative logic, ensure that no future change to the contract could cause it to require more computation than the limit.
-
Failures in logic: Examine the logic of your contracts closely to ensure that they cannot get into a blocked state. For example, if a contract requires a user to call one entrypoint and then another, ensure that the contract does not become blocked if the user calls the first entrypoint and then never calls the other.
-
Overflows of large numbers: Tezos can handle arbitrarily large integers and natural numbers. However, amounts of tez are limited to a 64-bit signed integer. This limit usually comes into play only in intermediate values, amounts that are used in mathematical calculations. You must ensure that calculations that use large amounts of tez don't overflow and cause operations to fail and block the contract. If necessary, you can use integers or natural numbers for large numbers in calculations.
-
Dependencies on other contracts or accounts: If the contract depends on other contracts, make sure that failures in those contracts do not block the contract permanently. You may need to include a way to modify the contract to account for changes or failures in other contracts. For example, if the contract calls another contract, consider including a way to change the address of the target contract so you can change it later if necessary. Similarly, if the contract has an administrator account, consider including a way to change it.
Contracts can also become blocked by waiting on some interaction. Suppose a contract holds an auction for one item at a time, and when the auction completes, it waits for the winner to pay and claim the item. If the winner does not do anything, the contract may become blocked. In this case, make sure there is another way to unblock the contract that does not depend on a single account.
In the same way, remember that if one operation in a chain fails, the entire chain fails. For example, imagine that a contract creates multiple operations to send rewards to a group of beneficiaries as the result of a single transaction. If any one of those operations fail, they all fail. A better way to handle this use case is to allow the beneficiaries to call an entrypoint to claim their own rewards.
Differences in blockchain programming
Here are some notable ways in which blockchain programming on Tezos is different from other types of programming:
-
Error handling: As described in Handling errors, exception management on Tezos is very different from other programming platforms. Instead, Tezos uses failures, which immediately cancel operations and revert the entire chain of operations that led to the failure. In this way, even if an operation succeeds, it can still be reverted if future operations in the chain fail.
-
Options and variants: Partly as a way of preventing errors, Tezos, its Michelson base language, and its higher-level languages use option and variant types, which some programmers may not be familiar with. Check the documentation for the language that you are using for more specific information.
For example, division by zero is not defined. Some languages throw an exception if you try to divide by zero. By contrast, Tezos languages return an option type as the result of division. The option is the
None
option if the result is undefined or theSome
option with the result of the division. Even if you are confident that your code will never divide by zero, you must still handle both options. For more examples, see Options -
Rounding and decimals: To prevent rounding errors, Tezos does not use floating-point numbers. All numbers, including amounts of tez (stored internally as mutez, or one-millionths of a tez), are stored as integers or natural numbers. For more information, see Numeric data types: int and nat.
Nevertheless, even when using integer amounts, it can be tricky to do mathematical operations like splitting amounts between participants. For example, if you want to split an amount of tez 60%-40% between two accounts, don't give one account 60% of the amount and the other 40%, because remainders may make the totals inaccurate. In cases like this, send the first amount and then use subtraction to determine the remaining amount to send to the second account.
-
Bitwise instructions: Bitwise instructions change the individual bits of a binary value. For example, some bitwise instructions shift bits to the left or right by a certain number of positions, filling the vacated bits with zeroes. However, left shifting can cause an overflow, which can cause unexpected results or errors when the contract runs in Michelson. To avoid this problem, check the size of the input and the shift amount before applying bitwise instructions.