Bitcoin OP_CAT Use Cases Series #3: Vaults
Following our series #1 and #2, we demonstrate how to construct non-custodial vaults, to provide enhanced security for stored bitcoins. They are typically used to protect against theft by requiring a time delay to access the funds. We can think of vault smart contracts as special accounts whose keys can be neutralized if they fall into the hands of attackers. Vaults are Bitcoin’s decentralized version of calling your bank to report a stolen credit card, rendering the attacker’s transactions null and void. This disincentives key theft in the first place, as attackers know they cannot get away with theft.
How Vaults Work
Funds locked in a vault contract can be accessed using either of two keys: a vault key, which is intended to be kept online and stored in a hot wallet, and a recovery key, which is kept securely offline in a cold wallet and used only for recovery. Typically, the vault key is used to create transactions that spend coins from the vault. Regardless of which key is used, any funds spent from the vault must pass through a time lock that holds the funds for a fixed period, such as 24 hours. This mechanism ensures that if a malicious actor obtains the vault key, they must broadcast a time-locked transaction on the blockchain before gaining access to the funds. This gives the vault owner a 24-hour window to detect the unauthorized movement of their funds and take action. During the time lock period, the contract allows the funds to be redirected to another address using the recovery key.
To spend bitcoins from a vault, two sequential steps are required:
- Issue a withdrawal request to move coins out of the vault through a transaction known as an unvault.
- Wait for a predefined period (called the unvaulting period), such as 24 hours, after the first transaction is mined before moving the coins out in a subsequent transaction.
The first transaction indicates an attempt to transfer the coins and gives the owner a chance to block the second transaction that would complete the transfer. Step 1 is similar to transferring money from a savings account to a checking account before spending it, while step 2 provides a 24-hour window to revert an unauthorized payment made from the checking account.
Implementation
The implementation of the vault mechanism using sCrypt involves three smart contracts: Trigger, Complete, and Cancel. Each is a leaf in the vault taproot as shown below.
Using the covenant trick (Line 16–17), we validate the trigger transactions
- contain two inputs and two outputs.
- the amount in the first input matches the amount in the first output.
- the address of the first input is identical to the address of the first output.
The Complete transactions must satisfy the following properties:
- having two inputs and one output
- the previous transaction is a Trigger transaction and contains the destination address in the second output. We parse the content of the previous transaction in line 20–28
- the amount in the first output of the previous transaction matches the single output amount in the current transaction.
Cancel transactions must ensure
- there are exactly two inputs and one output
- the amount of the first input matches the amount of the output.
A single run results in the following transactions:
- Trigger Transaction ID:
- Complete Transaction ID:
The full code of our vault implementation can be found at https://github.com/sCrypt-Inc/cat-contracts. It has been previously implemented on Bitcoin SV, which has a similar but different set of opcodes.
Script versions
There are alternative implementations in bare scripts, like purrfect_vault from Taproot Wizards. One major benefit of using sCrypt for implementing these vaults is its readability and maintainability. Traditional scripts are often difficult to read and modify.
Update 8/31/2024: support partial withdrawal
Our implementation above only allows withdrawing the full amount in a vault. A “partial withdrawal” in the context of Vault refers to withdrawing a portion of your Bitcoin vault holdings while leaving the rest intact. This feature is similar to withdrawing a partial amount from any cryptocurrency wallet, allowing users flexibility in managing their funds. We change the complete transaction, where only part of the fund is withdrawn to the target and the rest goes back to the vault.
Code change is available at https://github.com/sCrypt-Inc/cat-contracts/pull/1/files. Some sample transactions are listed below:
- Trigger Transaction ID:
- Complete Transaction ID:
- Cancel Transaction ID: