Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Breaking Change Tracker

Erik Marks edited this page Dec 20, 2019 · 4 revisions

This page documents how to migrate your code when we introduce breaking changes. We try to do this as little as possible, but given that we're still in early beta, breaking changes may be introduced at any time.

Each Migration has a Summary, listing the relevant changes in bullets. If a bullet is marked [REQUIRED], your Snaps and/or Snap-enabled dapps will break unless you make the suggested change.

Please keep in mind that, at this early stage, it is too costly to thoroughly QA all changes to metamask-snaps-beta#develop. Consequently, we may introduce inadvertent breaking changes. Therefore, we recommend clearing your plugin and permissions state in the Snaps beta extension before rebuilding with the latest develop. Please reach out if you have any questions or concerns.

December 20, 2019 - No Longer Reinstalling Installed/Running Snaps

Migration

Summary

  • [REQUIRED] In order to reinstall Snaps, you have to remove them first.
    • We recommend using the Delete All Plugins button in the main extension view. Specific Snaps can also be deleted by deselecting them in the Snaps settings page and clicking Update.
    • Snaps are still installed using wallet_enable or wallet_installPlugins.
    • In the future, we may add a way for installed/running Snaps to be reinstalled via RPC requests and user confirmation. At the very least, we will add a way for dapps/other Snaps (collectively called external domains) to require specific Snap versions.
  • You may notice performance improvements with this update, and there may be less of a need to refresh the MetaMask extension in chrome://extensions after removing Snaps.

December 12, 2019 - New Snap Installation Flow

Migration

Summary

  • [REQUIRED] When connecting your dapp to MetaMask, replace all references to wallet_requestPermissions with wallet_enable.
    • You can leave the parameters to wallet-enable as-is, but you can also replace wallet_plugin_pluginName-like keys with { wallet_plugin: { [pluginName]: {} }.
  • When calling a plugin RPC method, you should use the new wallet_invokePlugin method. For example:
    • Instead of ethereum.send('wallet_plugin_pluginName', [params]), do ethereum.send('wallet_invokePlugin, [pluginName, params].
  • With the above two changes, you don't have prefix your plugin identifiers with wallet_plugin_, which we think is much better!

All examples in snaps-cli have been updated to use the new required and preferred APIs.

Full Specification

wallet_requestPermissions and wallet_installPlugins

Previously, Snaps were installed by adding their permissions to wallet_requestPermissions requests. This created messy methods and workflows in our backend, and made it difficult for API consumers to know whether plugin installation succeeded.

To solve this problem, Snap installation has been broken out into its own RPC method, wallet_installPlugins. In addition, we've added some syntactic sugar so that dapps don't need to concatenate strings in order to request plugins:

interface IRequestedPlugins {
  [ pluginId: string ]: {},
}

Here, pluginId is just the plugin origin string (we'll figure out a better solution for that at some point in the future). Pair this with:

interface IRequestedPermissions {
  [ permissionName: string ]: {},
  wallet_plugin?: IRequestedPlugins
}

interface IResolvedPlugins {
  permission?: string, // the name of the corresponding permission
  error?: Error, // if installation fails, the associated error
}

ethereum.send('wallet_requestPermissions',[ IRequestedPermissions ]) => IOcapLdCapability[] // an array of permission objects

ethereum.send('wallet_installPlugins',[ IRequestedPlugins ]) => IResolvedPlugins

wallet_plugin is the key in the permissions request object where you put the plugin names. Consider this example:

await ethereum.send('wallet_requestPermissions', [{
  wallet_plugin: { 'http://localhost:8084/package.json': {} },
  eth_accounts: {}
}])

await ethereum.send('wallet_installPlugins', [{
  'http://localhost:8084/package.json': {},
}])

wallet_enable

Now, to avoid having to make two requests just to connect your dapp, we introduce the wallet_enable method:

ethereum.send('wallet_enable', [ IRequestedPermissions ]) => {
  accounts: Array<string>,
  plugins: IResolvedPlugins,
  permissions: Array<IOcapLdCapability>
}

To rewrite our example above:

await ethereum.send('wallet_enable', [{
  wallet_plugin: { 'http://localhost:8084/package.json': {} },
  eth_accounts: {}
}])

So, what exactly does this do? In detail, wallet_enable:

  • 1. Unwraps the wallet_plugin syntactic sugar into actual permissions
  • 2. Makes the permissions request
  • 3. Attempts to install any requested plugins
  • 4. Returns the wallet_enable return object, with information about permissions, plugins, and accounts
    • If errors were encountered installing a specific plugin, its object (keyed by its name) will include an error property.
    • If the permission request fails completely, the entire request fails.

To migrate, all you have to do is request wallet_enable instead of wallet_requestPermissions when your plugin-enabled dapp connects to MetaMask. You don't even have to change the params, because we still allow the wallet_plugin_-prefixed permission names to identify plugin for permissions and installation. However, you have to use either the wallet_plugin: { ... } sugar or wallet_plugin_-prefixed keys; using both in the same request will throw an error.

wallet_invokePlugin

Finally, we introduce one more new RPC method:

ethereum.send('wallet_invokePlugin', [ pluginName: string, pluginMethodParams: any ]) => any

Rather than making RPC requests to plugins by send a request for a method like wallet_plugin_pluginName, we expose an RPC method wallet_invokePlugin that takes the pluginName as a param. For example, in hello-snaps, instead of:

ethereum.send({
  method: 'wallet_plugin_http://localhost:8081',
  params: [{
    method: 'hello'
  }]
})

Do this:

ethereum.send(
  'wallet_invokePlugin',
  [
    'http://localhost:8081',
    { method: 'hello', params: [] }, // params could be omitted here
  ],
)

In this way, you don't have to concatenate strings with wallet_plugin_ to talk to your plugins. We think this is much better, and hope you will too!