dharma-smart-wallet

An upgradeable, meta-transaction-enabled smart wallet for earning interest on stablecoins while retaining custody of funds, with an added security backstop provided by Dharma Labs.

dharma-eng/dharma-smart-wallet https://www.dharma.io/smart-wallet
starsStars 151
forksForks 29
watchersWatchers 151
current-versionCurrent version 1.5.0
total-releasesTotal releases 2
open_issues_countOpen issues 8
dateFirst release 2019-11-15
dateLatest release 2020-02-11
updateLast update 2020-12-22

Dharma Smart Wallet (dharma-smart-wallet)

An upgradeable, meta-transaction-enabled smart wallet for earning interest on stablecoins while retaining custody of funds, with an added security backstop provided by Dharma Labs.

Summary

The Dharma Smart Wallet is a 2/2 "multisig" smart contract, controlled by the user's Dharma Key Ring and by Dharma Labs, that:

  • allows users to make deposits and mint Dharma Dai or Dharma USD Coin by simply sending Dai or USDC to their smart wallet address, even before a contract has been deployed to that address
  • allows users to make Dai or USDC withdrawals without paying for gas, by providing signed messages that are validated against both the user's Dharma Key Ring contract and against the Dharma Key Registry and relayed by Dharma
  • allows users to recover their account if access is lost or compromised after a three-day timelock, or to opt out of account recovery entirely, via the Account Recovery Manager
  • allows for upgrades to all user smart wallets at once, without requiring any action on behalf of the user, and with a seven-day timelock prior to each upgrade, using the Upgrade Beacon Controller Manager

The Dharma Key Ring is an N/M "multisig" smart contract, controlled and configured by the user and set as one of the two signatories on their Dharma Smart Wallet, that:

  • provides users with flexible, secure access to their Dharma Smart Wallet so that they retain custody over their own funds at all times
  • allows users to take actions on their smart wallet without paying for gas or submitting transactions themselves, simply by providing signed messages that map to keys that they have set on their key ring
  • allows users to add multiple devices to their Dharma Smart Wallet using existing devices so that their keys stay on their own devices, not in the cloud
  • allows for upgrades to all user key rings at once, without requiring any action on behalf of the user, and with a seven-day timelock prior to each upgrade, using the Upgrade Beacon Controller Manager

These contracts have been audited by Trail of Bits - see their security assessment for more information.

Table of Contents

Contract Deployment Addresses and Verified Source Code

Core Contracts Implementations Factories
DharmaUpgradeBeaconControllerManager AdharmaSmartWalletImplementation DharmaSmartWalletFactoryV1
DharmaUpgradeBeaconController DharmaSmartWalletImplementationV1 DharmaSmartWalletFactoryV2
DharmaUpgradeBeacon DharmaSmartWalletImplementationV2
DharmaKeyRingUpgradeBeaconController DharmaSmartWalletImplementationV3
DharmaKeyRingUpgradeBeacon DharmaSmartWalletImplementationV4
DharmaUpgradeBeaconEnvoy DharmaSmartWalletImplementationV5 DharmaKeyRingFactoryV1
DharmaKeyRegistryV1 DharmaSmartWalletImplementationV6 DharmaKeyRingFactoryV2
DharmaKeyRegistryV2 DharmaSmartWalletImplementationV7 DharmaKeyRingFactoryV3
DharmaAccountRecoveryManagerV2 AdharmaKeyRingImplementation
DharmaEscapeHatchRegistry DharmaKeyRingImplementationV1

Overview

The Dharma Smart Wallet and Dharma Key Ring are designed with the following assumptions in mind:

  • Dharma users will share custody of their smart wallet, with Dharma Labs serving as a security backstop, in order to better protect their funds from loss and from external adversaries and to simplify the process of earning interest on their stablecoins. The security backstop gives users the liberty to store keys in contexts that would be insufficient taken in isolation, such as in their web browser, while still maintaining non-custodial assurances that, were Dharma's key to ever become compromised, their funds would stay securely in their possession. If users decide they would rather use a fully self-custodial wallet and handle the details themselves, they will migrate away from the smart wallet by transferring their funds to a new address, or wait for upgrades to the smart wallet that unlock options for greater degrees of self-custody.
  • Users are content to let Dharma make upgrades to their smart wallets, and do not require opt-in upgrades. However, a seven-day timelock on upgrades will let users "opt-out" of unwanted upgrades by giving them a window of time to withdraw their funds from the smart wallet.
  • The initial wallet implementation uses a subsidized ).
  • The wallet validates protected actions using standard ethereum message signatures (EIP-1271, which allows for the Dharma Key Ring to hold multiple signing keys and to eventually support per-key and per-action-type permissions and thresholds.
  • The smart wallet is not set up to do anything too fancy, like deploy contracts or run arbitrary code - it only needs to be able to make transfers and calls into other contract accounts. In particular, interactions with Dharma Tokens and Compound V2 contracts are mediated via custom, streamlined functions.

Dharma Smart Wallet

The current implementation of the Dharma Smart Wallet works as follows:

Dharma Key Ring

The Dharma Smart Wallet is controlled by the Dharma Key Ring:

  • It implements EIP-1271, which allows for the Dharma Key Ring to hold multiple signing keys and to eventually support per-key and per-action-type permissions and thresholds. It is not intended to be used for submitting transactions, but instead as a source for validating signatures passed to it by the Dharma Smart Wallet and other sources.
  • It differentiates between two primary key types (note that "dual" keys can be both types at once):
    • Admin keys, which are used to add or remove other keys and to change thresholds or other properties of the key ring
    • Standard keys, which are used to verify most external signatures that it receives based on the data provided (notably, attempts to set a new user signing key for the smart wallet must provide signatures from Admin keys)
  • Key Ring addresses are also counterfactual, based on their initial signing key, and are only deployed once they are needed to make a withdrawal or to add an additional key. They can be deployed by anyone, using a Dharma Key Ring Factory - furthermore, anyone can deploy their own Dharma Key Ring for use in other applications.
  • All required signatures must be provided at the same time - this enables actions to be taken in a single transaction. Provided signatures must be ordered based on the signing address, from lowest to highest, in order to be considered valid.
  • To protect against replay attacks, every call to the key ring to execute an Admin action will include a nonce that must match the current nonce on the key ring, and each time the smart wallet is called with enough gas and valid signatures that nonce will be incremented. Note that gas is not an input to signatures on the key ring, since there are no external calls made by the key ring during execution, so calls with insufficient gas can be replayed until they succeed or until another admin action is taken that increments the nonce.

Upgradeability

Both the Dharma Smart Wallet and the Dharma Key Ring are upgradeable:

  • Like most upgradeable contracts, they utilize a proxy mechanism, where each instance performs a DELEGATECALL to an updateable "implementation" contract when called. However, in contrast to most upgradeable proxy contracts, which store an implementation address and an admin address that can change that implementation, smart wallet and key ring instances have no proxy-specific state, and instead retrieve their current implementation from a dedicated Upgrade Beacon contract.
  • Each upgrade beacon is a minimally-simple contract that has a hard-coded controller and a single storage slot that represents the current implementation. Only the controller may update this storage slot, and the upgrade beacon will return the address contained at that storage slot for any other caller.
  • Each Upgrade Beacon Controller is an ownable contract that emits events related to each upgrade, enforces some conditions as to what upgrades are allowable, and enables the current owner of the controller to transfer ownership to a new owner.
  • The Dharma Upgrade Beacon Controller Manager owns each Upgrade Beacon Controller, and can also support other, future upgrade beacon controllers. It enforces configurable time-locks for each protected function - these include functions for making upgrades, for transferring ownership of controllers, and for modifying the default timelock intervals and other parameters.
  • While all new upgrades have a seven-day timelock, there are two notable exceptions:
    • If a bug is introduced in a new upgrade, the Dharma Upgrade Beacon Controller Manager can immediately "downgrade" and roll back to a prior implementation (with the exception of implementations that have been explicitly blocked due to discovered vulnerabilities or other undesired features).
    • If a critical vulnerability is discovered, or if 90 days go by without a "heartbeat" being triggered on the Dharma Upgrade Beacon Controller Manager, an "
  • Prior to each upgrade, a prospective implementation needs to first be deployed and registered as a potential upgrade candidate. This gives the community a chance to review the implementation, and to raise any potential concerns or opt-out of the upgrade by withdrawing their funds.
  • The Dharma Escape Hatch is an opt-in feature that enables a smart wallet to call into the

Install

To install locally, you'll need Node.js 10 through 12 and Yarn (or npm). To get everything set up:

$ git clone https://github.com/dharma-eng/dharma-smart-wallet.git
$ cd dharma-smart-wallet
$ yarn install
$ yarn build

Usage

To run tests locally, start the testRPC, trigger the tests, run the linter, and tear down the testRPC (you can do all of this at once via yarn all if you prefer):

$ yarn start
$ yarn test
$ yarn lint
$ yarn stop

You can also run code coverage if you like:

$ yarn build
$ yarn coverage

There is also an option to run tests against a fork of mainnet - be warned that these tests take a very long time.

$ yarn forkStart
$ yarn test
$ yarn stop

Finally, there is an option to run code coverage against a mainnet fork (same caveat as above):

$ yarn build
$ yarn forkCoverage

Example Contracts and Notable Transactions

Example Contracts:

Notable Transactions:

Additional Information

Have any questions or feedback? Join the conversation in the