Search
  • Jonathan Fischoff

50 Ways to Lose Your Love...laces

Updated: Nov 16, 2021

Smart contracts are great. We love them at Canonical. It is pretty much all we write, but they can be dangerous. Without proper care and precautions, you're going to have a bad time and end up poorer.


You don't have to look too far to find an example of an exploitable smart contract. If you use the `alwaysSucceeds` contract shown in the "How to write Plutus transactions" documentation, you will lose funds, because a bot will immediately steal them.


`alwaysSucceeds` is a trivially exploitable contract, so it is not surprising that an attacker can steal the funds. However, what users might not realize is how smart contracts engender an adversarial environment. Plutus smart contracts have been live for a little over a month, and already a hostile environment has developed, where bots are running wild to steal funds.


Sadly, as more money is locked in smart contracts we can expect attackers to become more sophisticated and more prevalent.


The Missing Input Exploit

A less obvious exploit can occur if a contract checks the outputs of a transaction but not the inputs.


Consider the case of a NFT standing offer contract.


A standing offer affair takes two transactions.


In the first transaction, the NFT buyer makes an offer for an NFT by sending Ada to script, thereby locking it there for the NFT owner to accept.


Or, at least, that is the theory.


For the exploit to work, the user does not send Ada, they merely set the transaction output hash.


When the NFT owner decides to accept the offer, the transaction building code of their wallet or browser tends to work the following way:

  • The outputs of the transactions are specified.

  • The script input UTxO is specified.

  • The UTxOs at the user's wallet address are used to fill in the missing inputs.

Even though the script address is missing the expected Ada for purchasing the NFT, the transaction building code will just use the Ada from the NFT owner's wallet.


Since the smart contract only checks that the required outputs are present in the transaction, everything will succeed.


The attacker will get the users NFT, and the former NFT owner will get as payment the Ada from their wallet.


The Defaults Are Not Your Friends

Exploits are the most exciting way to lose funds; however, they are not the most common.


The most common way funds are lost, is when they become trapped forever.


For typical wallet addresses this occurs when someone loses their private keys.


For script addresses more information is needed to unlock the funds, so there is more room for error.


By default, the datum is not stored when locking assets, only the datum hash (learn more about his in Plutus 102). However, the full datum is needed to unlock assets. Because the assets will be lost forever if the datum is lost, it is safest to store the datum as metadata, although this is not the default.


Another gotcha with datums: you must add a datum hash. If you forget, your funds are locked forever.


Additionally, the smart contract code is not stored when locking assets. However, it is needed to run the validator to unlock the assets. The safest method to ensure that the script is always available to unlock the assets, is to store it as metadata as well.


Storing additional information in the transaction will increase the fees, but the alternative can be devastating.


Assume the Worst

Many smart contracts that swap assets between users have a cancel operation. The locker sends asset to the script's address, and if they later decide they no longer want to trade assets, they can create a cancel transaction to return their assets.


A smart contract developer might decide, having the best of intentions, to ensure a user sends assets back to their wallet when canceling.


On the surface, this makes sense: the smart contract is ensuring that a good thing happens. The user will get their assets back to their wallet.


The problem is, it's easy for the transaction creation code to have bugs. So if the asset output check is based on the datum, and the datum is invalid, the funds could be lost.


A simple check for only the signature of the owner might have prevented the loss of funds.


The Limits of Computation

The scariest way funds can be locked with smart contracts is when memory limits are hit.


This can happen even with the simplest validator possible: a constant validator that returns `True`.


If enough assets are sent to a script address, during validation, the protocol memory limit will be hit. There is no way to free these assets, short of begging IOG to increase the limits or bribing SPOs. It is unclear how to know in advance this will happen. See this github issue for more details.


Be Safe Out There

If you're starting out as a Plutus developer, please, please use the testnet. You will still lose real assets on mainnet (it is a Plutus rite of passage), but it is much easier to lose assets than most new developers realize.


If you're looking for guidance on how to avoid these issues, or would like help with Plutus development in general, don't hesitate to contact us at Canonical.


Stay safe frens.

500 views0 comments

Recent Posts

See All

Update: A Cryptoslate article incorrectly stated "Cardano has a universal app exploit". This is not true. There is a common mistake dApp developers keep making. The rapid development of new dApps with

It is often stated that one writes Plutus smart contracts in Haskell. This is cited as one of the benefits of using Haskell for on and off-chain code: you can reuse the same code. Largely, this is tru

There is a misconception in the Cardanosphere that the Plutus Application Backend (PAB) is required to build dApps. It is true that building dApps on Cardano is difficult. It is novel technology with