Contract Safety Checklist

I put together the initial safety checklist, and @MathieuG kindly suggested creating a GitHub repository to enable contributions from others.

You’re welcome to create a PR or fork the repository. This thread can also serve as a helpful reference for the list.

  • Identify and list all entry points in the contract.
  • Identify input and output restrictions, if any.
  • Identify version check requirements (timelocks, etc.)
  • Assess future upgrade vulnerabilities
    • Example(s):
      • Relying on commitment length, an attacker can update the nftCommitment in a way that creates a Denial of Service). Use: require(tx.inputs[x].nftCommitment.length == 40); or require(tx.inputs[x].nftCommitment.length == <required_value>);
  • Determine who can inject inputs into the transaction, whether it’s p2pkh or p2sh.
    • Example(s):
      • In cases where someone wants to store the PKH from an input into an NFTCommitment, they’ll probably split the lockingbytecode but if the input is from a p2sh then the stored value in the commitment will be incorrect leading to a DoS. require(tx.inputs[x].lockingBytecode.length == 25); or require(tx.inputs[x].lockingBytecode.length == <required_value>);
  • Detect potential UTXO injections.
  • Identify unintentional burns (FT/NFT)
  • Identify unintentional minting
  • Check for category leaks
    • Example(s): Tokens might be sent to unintended destinations.
  • Check for NFT capability changes and ensure predictability
  • Find deadlocks due to state updates
    • Example(s):
      • A mutable NFT is converted to an immutable state, causing a limbo state for other contracts that still expect a mutable NFT.
      • Changes in NFT commitment fail to meet the checks of other contracts, rendering it unusable.
  • Verify the Genesis configuration to prevent misconfigurations that could lead to failed states.
  • Check for mathematical errors.
  • Avoid split errors without checking the length
  • Implement length validation for function arguments
2 Likes

It might be interesting to develop a security footprint scoring system, similar to VM computation budget.

For apps with organic engagement, the willingness to engage with the contract or “trust” appears to be inversely proportional to the length of the contract.

Below is a snapshot from a TLV tracker.

The unlockng bytecodes for the below dapps are 6, 34, 530, 53 bytes respectively.

So even through pat’s hodl app pays no yield, users are still trusting the much smaller contract more than a more complex contract.

Badger has also been offering consistently higher yields, but the vault much more complex than FBCH.

image

This checklist might be simpler to use if it were a set of nested questions.

That way designers might skip past portions that aren’t relevant to the scope of their contract.

For example,

  • Does the contract use timelocks?
    • Is an OP_TXVERSION greater or equal to 2 enforced?
      • Is an OP_TXVERSION greater than 2 allowed?
    • Is logic using block height, MTP timestamps or both?
      • Is the range limited to a single domain?
  • Does the contract use CashTokens?
    • If yes:
      • Is the contract specific to a single category?
        • How is the category enforced?
      • Are outputs allowed to genesis new token categories?
      • Are outputs allowed to create NFTs?
    • If not:
      • Would there be unintended consequences from CashToken outputs from the contract?

etc …

So in the above tree, if a user is doing a multi-party escrow with no timeout where CashTokens are NOT supported, then they might skip some portions entirely.

2 Likes

Using nested questions appears to be a more effective approach than a simple linear list.