A simple & safe multisig Ethereum smart contract for hardware wallets

Unchained Capital logo

First published: 03/08/2018
| Last updated: 01/17/2023
| -- min read

note: Unchained Capital does not currently support Ethereum or any other altcoins in any way. We supported ETH loans briefly in 2018-19 but officially discontinued all support in Q4 2019.

Unchained Capital has released an open-source Ethereum smart contract implementing 2/3-multisig designed to directly interface with Trezor hardware wallets.

If you have a Trezor, you can try this contract right now, for free, via our hosted dApp.

Developer? Hacker? Paranoiac? Check out the source code on GitHub.

Details below!

What is multisig and why use it?

In theory, cryptocurrencies can be one of the safest ways to store wealth. By safeguarding a short list of English words, anyone can protect millions of dollars in wealth and rest assured that no hacker or government can take it from them. In practice, it’s never so easy.

This is because holding cryptocurrency safely amounts to protecting the private keys used to secure addresses in blockchains. Some investors are comfortable delegating security to a 3rd parties such as exchanges. For those who prefer to protect their funds themselves, there are various schemes such as air-gapped laptops, purpose-built hardware wallets such as Trezor or Ledger, and even just pen & paper (“wallet words” and other private key backups).

One problem that most of these schemes all share is that they are designed for single-signature addresses and so rely on protecting a single private key. No one is perfect, so what happens when you lose access to that private key? Mark Frauenfelder’s wonderful article on losing his Trezor’s PIN & wallet words vividly illustrates the horrors of finding out you have locked yourself out of your funds because you can no longer access the single private key protecting your addresses.

A better solution would use a 2/3-multisig quorum consisting of three separate private keys, held by three separate people, and requiring any two to spend. This provides both security and redundancy since compromising any one key/person does not break the quorum: if one key is stolen or lost, the other two keyholders can sweep funds to another address (protected by a new quorum) by mutually signing a transaction moving the funds. Imagine the following scenario:

You live in San Francisco and have this brother in New York who told you to buy crypto early. Now you both have so much of it you worry. So you both pool your holdings into a 2/3-multisig account with each of you holding a key and a childhood friend you both trust holding the 3rd, all in hardware wallets. Whenever you want to trade you contact each other using secure channels and arrange a spend which you both sign with your separate keys. If either of you loses your key the other can collaborate with your friend to sweep your funds to a new quorum. Your friend can never spend the funds without at least one of you also signing.

This scenario presents a highly secure way to hold large amounts of cryptocurrency as a private individual with redundancy against loss without having to engage any 3rd party companies. It’s a powerful testament to their future potential that blockchains make this possible today.

If you hold cryptocurrency on your own and you trust at least two other people, you could be doing it better.

Except that it’s not that easy for private individuals actually to do what’s outlined in the scenario above. Most end-user wallets assume the context of a single private key. Those wallets which offer multisig often can’t directly talk to a hardware wallet.

And then there are the bugs. This is an especially pernicious problem for the Ethereum ecosystem.

Why has multisig been difficult to implement in Ethereum?

Multisig is a general concept, but the details of how it is to be implemented differ across blockchains. Bitcoin’s Script is very limited, and so the implementation of a 2/3-multisig address in a Bitcoin P2SH script is broadly similar across all users and contracts: there’s just “one way to spell it.”

A blockchain such as Ethereum has a much more capable virtual machine and is designed for more complex computing. This has created an embarrassment of riches for the Ethereum community. There is so much code out there, but no canonical smart contract which everyone trusts to use for multisig: there are “many ways to spell it,” and it’s not clear which to trust.

The Parity hacks of 2017 (1, 2) were especially troubling, because they showed that the default multisig implementation shipping with one of the leading Ethereum clients was horribly broken. How could a catastrophe like this have happened, not once, but twice? And to the same part of the codebase ultimately due to the same kind of attack vector? Is the Parity team just bad? Is Solidity designed poorly? Is the EVM difficult to write code for? Is a world computer compatible with the idea of sound money?

These are the questions we were asking ourselves last year at Unchained Capital as we sought to deploy our own multisig Ethereum contracts to support lending against ETH as a form of collateral (in addition to BTC).

Unchained’s Ethereum Multisig Smart Contract

We first attempted to use an existing implementing of 2/3-multisig but we weren’t happy with any of the ones we found:

  • Parity’s Multisig Contract — We use Parity and so were originally excited about using its built-in multisig contract. Ahhhh, Parity, we like you as a node, but you’ve lost our trust on smart contracts at this point: too complex, clearly not enough testing. Nuff said.
  • ConsenSys’ Multisig Wallet — the functionality is excessive for our use case: contract owners can be changed (the root cause of the Parity bugs) and most state is kept within the contract. The canonical repo has no unit tests and very little content besides a .sol file. It also hasn’t been committed to in 5 months. The Gnosis’ Multisig Wallet is the evolution of ConsenSys’ multisig wallet and is tested & under active development but retains the unnecessary complexity of the original.
  • BitGo’s Multisig Contract — has full unit-test coverage and recent development activity. But the design is again overly complex for our needs: multiple contracts are used, ERC20 tokens are integrated, &c. The contract also assumes that the account broadcasting spending transactions is a member of the 2/3-quorum. This assumption is false for us — we want to use hardware wallets and utilize a separate hot-wallet for all transaction broadcasts. In our view, the ideal contract would have absolutely no relation to the hot wallet which created it or which spends from it.

We weren’t the only ones who felt this way — see this excellent post by Alex Miller of Grid+ which urges for a low-functionality, stateless, simple design philosophy for Ethereum multisig, a philosophy that would result in truly safe contracts.

So we decided to write our own contract. Here are the design guidelines we adopted:

  • No external contracts. Calling methods of other contracts inside methods in your own contract is an amazing feature of Ethereum but should not be required for our simple use case. This also avoids exposing us to entire classes of bugs.
  • No libraries. Extending the last guideline, our contract has no upstream dependencies other than Solidity itself. This minimizes the chance of us misunderstanding or misusing some piece of library code. It also forces us to stay simple. Both good things 🙂
  • Minimal internal state. Complex applications can be built inside of Ethereum smart contracts…but should they? Storing minimal internal state allows our contract’s code to be simpler, and to be written in a more functional style, which is easier to test and reason about. Combined with not using external contracts or libraries, this further minimizes concurrency and reentrancy bugs.
  • Uses cold-storage. The Ethereum client which creates or spends from the contract has no special rights or access to the contract. Authorization is handled by directly signing messages with hardware wallets. (Trezor-only for now; Ledger support is planned.)
  • Complete end-to-end testing. The contract itself is exhaustively unit tested.

To many Ethereum developers, the above list might seem like it was written by a Luddite. What’s the point of using Ethereum if you’re going to disallow using all its coolest features?

But this is exactly our point! We are not trying to build a complex application, we are building a vault. A vault needs to be secure above anything else and so by minimizing the features of our vault, we maximize its security.

Unchained’s Ethereum Multisig Vault Bug Bounty

We open-sourced our multisig contract and created a dApp to help people use it because we want to maximize the exposure of our code to the Ethereum community. We believe this will increase the security of our contract for all.

We are also launching our bug bounty program with an initial award pool of $150,000. If you discover bugs (whether critical or trivial) in our Ethereum multisig contract, you can report them to us and earn a bug bounty. If you are a security researcher, Solidity programmer, or hacker (black hat or white), we encourage you to investigate our smart contract, use our dApp, and try and break something 🙂

ETH holders: if the reception to our Ethereum multisig contract and bug bounty are warm and no significant issues are discovered, we plan to use the multisig contract to lend against ETH as collateral in the very near future.

To learn more about Unchained Capital and our crypto-secured loans, sign-up on our website. To stay up to date on Unchained Capital news and announcements, follow us on our Blog and Twitter.

Sign up to get notified for future blog articles.