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.
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 cDai or cUSDC from Compound 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 seven-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
- Example Contracts and Notable Transactions
- Additional Information
Contract Deployment Addresses and Verified Source Code
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 meta-transaction mechanism, where Dharma pays for the gas - in other words, there is no need to implement strict gas metering or extra fees in order to pay the transaction relayer back (as a matter of fact, the user won't have to deal with gas or transaction submissions at all).
- The wallet validates protected actions using standard ethereum message signatures (ECDSA + ecrecover + EIP-191) with replay protection baked in. Additionally, the Dharma Smart Wallet supports 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 Compound V2 are mediated via custom, streamlined functions.
Dharma Smart Wallet
The current implementation of the Dharma Smart Wallet works as follows:
- Smart wallet deployments and stablecoin deposits can be initiated by anyone who is willing to pay the gas. Smart wallet addresses are counterfactual based on the initial user signing key, and can be safely deployed by anyone using a Dharma Smart Wallet Factory, as withdrawals will atomically redeem and transfer the relevant stablecoin. Therefore, expect any Dai or USDC sent to the smart wallet address to quickly be converted to cDAI or cUSDC.
- Every withdrawal requires signatures from both the user and from Dharma Labs in order to be accepted. The signature component from the user is validated against their Dharma Key Ring, and the signature component from Dharma Labs is validated against the Dharma Key Registry. Either party may cancel a given signature at any time prior to execution.
- The Dharma Account Recovery Manager will only be used in the event that a user has lost access to their Dharma Key Ring or it has been compromised - it will not have any other control over the user's smart wallet. Furthermore, the Account Recovery Manager enforces a seven-day timelock before any account can be recovered. A user can also permanently opt out of account recovery through the Account Recovery Manager, though this is not recommended unless the user is highly confident in their own key management.
- Both required signatures must be provided at the same time - this enables actions to be taken in a single transaction, and also lets an account that is not a signatory on the wallet submit the transaction and pay for the gas. One of the signatures can be omitted if the submitting account is a valid signatory.
- To protect against replay attacks, every call to the smart wallet will include a nonce that must match the current nonce on the smart wallet, as well as an optional minimum gas amount, and each time the smart wallet is called with enough gas and valid signatures that nonce will be incremented. This applies even when the action being taken by the smart wallet reverts, in which case an event will be emitted.
- Additional features will be rolled out incrementally, like the ability to assume greater degrees of self-custody over the smart wallet or to designate alternate recovery mechanisms for the smart wallet.
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.
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
DELEGATECALLto 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 "Adharma Contingency" upgrade can be executed on both the smart wallet and the key ring simultaneously. These implementations strip out all but the most basic functionality and give the user direct, singular control over their smart wallet and key ring contracts - basically, it ensures that users can recover their funds, no matter what. A new upgrade can be performed by the Dharma Upgrade Beacon Controller Manager after 48 hours in the contingency state.
- 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 Dharma Escape Hatch Registry and designate an "escape hatch" account that can sweep the entire balance from the wallet by calling into the
escape()function directly. This gives users with an existing, secure key the option to exit the system at any point without needing to first secure Dharma's approval.
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
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 UpgradeBeaconProxyV1 smart wallet instance
- Example KeyRingUpgradeBeaconProxyV1 key ring instance
- V1 implementation set on Smart Wallet Upgrade Beacon
- First Dharma Smart Wallet deploy + deposit through Dharma Smart Wallet Factory
- First Dai and USDC withdrawals
- V2 implementation set on Smart Wallet Upgrade Beacon
- V0 implementation set on Key Ring Upgrade Beacon
- V1 implementation set on Key Ring Upgrade Beacon
- V3 implementation set on Smart Wallet Upgrade Beacon
- Dharma Account Recovery Manager ownership transferred to multisig
- Dharma Key Registry V2 ownership transferred to multisig
- V4 implementation set on Smart Wallet Upgrade Beacon
- Key Ring Upgrade Beacon Controller ownership transferred to Upgrade Beacon Controller Manager
- Smart Wallet Upgrade Beacon Controller ownership transferred to Upgrade Beacon Controller Manager
- Upgrade Beacon Controller Manager ownership transferred to multisig
Have any questions or feedback? Join the conversation in the Dharma_HQ Discord server. Also, see the "Why Smart Wallets Should Catch Your Interest" article for a more informal discussion around some of the motivations, features, and design decisions of the Dharma Smart Wallet.