Coinbase Logo

Ethereum Smart Contract interaction out of the coldest of storage

(This blog post reflects a talk that Max Blaushild gave at Devcon 5 in Osaka, Japan)

By Author


, October 14, 2019

, 4 min read time


At Coinbase Custody, security is our top priority. The strength of our battle-tested cold storage solution is why more customers choose us to hold their funds than any other custodian on the planet.

We use friction as a feature, introducing obstacles such as offline storage, single use addresses and human validation that make it extremely difficult for attackers to steal our customers’ funds.

This friendly friction comes with certain trade offs however. Our solution natively struggles with the sort of on-chain network participation that makes Ethereum great. Interacting with dApps like Maker Governance, or participating in signalling events like the Edgeware lockdrop require many transactions to be signed with an address that holds funds, which violates our one-transaction-per-address rule.

We’ve discovered a simple, yet effective pattern that allows us to bypass these restrictions. By leveraging the power and safety of smart contracts, our customers can have their cake (hold their crypto safely) and eat it too (use it to do neat things with smart contracts)!

How do we pull this off? Let’s dive into it!

Proxy Smart Contracts

We use smart contracts as a bridge between our cold storage system and these on-chain Ethereum contracts.

When we choose to integrate with a new network, like Maker Governance, we start by writing a customized smart contract that wraps around their smart contracts’ APIs like a glove. These ‘proxy’ smart contracts all come out slightly different, since they wrap different functionality, but share a few core similarities.

First, they are exceedingly simple. All storage variables are set when the contract is deployed, and never change. No branching logic is implemented in these contracts as well. Regardless of their simplicity, a 3rd party security audit is performed to ensure the safety and security of these contracts..

For instance, the proxy contract we used to allow our customers to participate in the Edgeware lockdrop was less than 30 lines of code with only one externally callable method:

Small and to the point

contract EdgewareSignalProxy { address payable public fundDst; address public fundSrc; address public admin; bytes public edgewareAddr; Lockdrop public lockdrop;

constructor( Lockdrop _lockdrop, address _fundSrc, address payable _fundDst, address _admin, bytes memory _edgewareAddr ) public { lockdrop = _lockdrop; fundSrc = _fundSrc; fundDst = _fundDst; admin = _admin; edgewareAddr = _edgewareAddr; }

modifier auth() { require(msg.sender == admin || msg.sender == fundDst, "Sender must be the fund destination or admin address"); _; }

function () external payable { require(msg.sender == fundSrc, "Sender must be the fund source address"); signal(); }

function signal() internal { lockdrop.signal(address(this), 0, edgewareAddr); }

function release() external auth { address(fundDst).transfer(address(this).balance); } }

Second, they are designed to interface with only a single cold address. This keeps all client funds segregated and maintains the security and auditability of all actions taken by this contract.

Third, and most importantly, these contracts are all configured to return funds to, and only to, a pre-set cold storage address. Securing this out-channel gives confidence that once we move funds into these contracts, their only possible destination is a Coinbase Custody Cold storage address.

Using our Proxy Smart Contracts

When onboarding a customer to a new network integration, we start by delegating them an address controlled by our Ethereum wallet service called Macbeth. This address never receives customer funds. Rather, it is used exclusively to make contract calls for our customer to their proxy smart contact. You can think of it as an “admin” address.

We then use this address to deploy a proxy contract on behalf of the customer. This address and the return Coinbase Custody Cold storage address are the only addresses able to call the contract:

modifier auth() { require(msg.sender == admin || msg.sender == fundDst, "Sender must be the fund destination or admin address"); _; }

Next, we process a standard withdrawal to send the customers funds from cold storage to the smart contract. Once the customer’s funds have been transferred to their proxy, the fun can begin!

To perform a network action, we simply use Macbeth to call the proxy contract method that facades the intended network method on behalf of our customer. In the instance of Maker Governance, we have the method on their contract that performs a vote:

Which is in turn wrapped by our proxy method:

function function vote(address[] memory yays) public returns (bytes32) // note both sub-calls note { bytes32 slate = etch(yays); vote(slate); return slate; } view raw(address[] memory issues) public auth returns (bytes32) { lock(); return; } view raw

When a customer wants to vote, they press a button on our UI without having to provide any keys or integrate with any Ethereum identity provider. We then handle all of the heavy lifting for them, grabbing the address of their proxy contract from the blockchain, forming their transaction and broadcasting it to the blockchain:

1*65c - 36HTs3DqRaj-icxQ

In conclusion

Our proxy smart contract pattern provides a frictionless user experience when interacting with Ethereum smart contracts. Moving the customers’ funds to a specialized proxy smart contract allows us to sign specific transactions behind the scenes, while maintaining the safety and security of client funds.

Through the use of this pattern, our customers have successfully participated in Edgeware Signalling, and now, Maker Governance. We have many more integrations planned for the future as well!

If you’re interested in helping us expand our bridge between cold storage and smart contracts, or just want to build cool things with blockchains in general, please apply for an open position at:

We’d love to have you!

This website contains links to third-party websites or other content for information purposes only (“Third-Party Sites”). The Third-Party Sites are not under the control of Coinbase, Inc., and its affiliates (“Coinbase”), and Coinbase is not responsible for the content of any Third-Party Site, including without limitation any link contained in a Third-Party Site, or any changes or updates to a Third-Party Site. Coinbase is not responsible for webcasting or any other form of transmission received from any Third-Party Site. Coinbase is providing these links to you only as a convenience, and the inclusion of any link does not imply endorsement, approval or recommendation by Coinbase of the site or any association with its operators.

All images provided herein are by Coinbase.

Coinbase logo