Polysnap: Invoking Polywrap Wasm Wrappers on the fly

An ETHIndia Hackathon Winner

by MetaMaskJanuary 23, 2023
feature

MetaMask Snaps is the roadmap to making MetaMask the most extensible wallet in the world. As a developer, you can bring your features and APIs to MetaMask in totally new ways. Web3 developers are the core of this growth and this series aims to showcase the novel MetaMask Snaps being built today.

Polysnap

Snap Repo: https://github.com/Niraj-Kamdar/Polysnap

Why did you build it?

Currently, developers need to create a snap for every new capability they want to add to MetaMask. Polysnap presents a better approach to code-reuse. A wide selection of Polywrap-empowered Wasm modules are stored on IPFS. Polysnap lets app developers execute any Wasm wrapper deployed on IPFS, on the fly and securely, with a simple JSON RPC call, without needing to build a new Snap.

Users of a dapp relying on the Polysnap don’t need to install multiple snaps but only need to install polysnap which can help reduce potential attack vectors. Dapp using Polysnap can be more secure than traditional snap because Polysnap gatekeep any actions related to host capabilities like internet access, wallet access, etc and would always require user-confirmation before executing any code that relies on these capabilities.

Dapp developers don’t need to ask users to install multiple Snaps for them to run their app, but rather they can just use one Snap–Polysnap–and run any Polywrap wrappers.

Snap developers are no longer restricted to JavaScript and the JavaScript ecosystem for Snap development. They can instead write Polywrap wrappers in languages like Rust, Go, and AssemblyScript, and call the wrappers using Polysnap.

Can you walk us through the technical implementation?

The Polysnap project consists of two elements:

  1. Polysnap (MetaMask Snap)
  2. Polysnap Demo Website

The MetaMask Snap is the main feature, and the focus of the project. The website is used to demonstrate the Snap.

The Snap

Polysnap can be divided into 3 different parts.

  1. Polywrap Client
  2. Plugin Wrapper
  3. WASM Wrapper

1. Polywrap Client

Polywrap is building a unified execution environment for the internet that is safe, open, and user-owned.

Polywrap allows us to create endlessly extensible software that runs anywhere using a Polywrap Client. The Polywrap Client supports running two types of wrappers:

  1. Wasm Wrapper - any sandboxed Wasm wrapper module built and deployed on IPFS using the Polywrap toolchain
  2. Plugin Wrapper - any host capability wrapper that exposes different host functionalities like HTTP, the filesystem, and JSON-RPC, to Wasm wrappers.

You can check out how we are using the Polywrap Client to invoke any wrapper with a simple JSON-RPC call to the Polysnap.

export const onRpcRequest: OnRpcRequestHandler = async ({
  origin,
  request,
}) => {
  switch (request.method) {
    case 'wrap_invoke':
      const invocation = request.params as unknown as InvokerOptions;
      const isApproved = await wallet.request({
        method: 'snap_confirm',
        params: [
          {
            prompt: `${origin}`.substring(0, 40),
            description: `URI: ${invocation.uri}\nMethod: ${invocation.method}`,
            textAreaContent: `Args ${JSON.stringify(invocation.args)}`,
          },
        ],
      });

      if (isApproved === true) {
        const client = await getClient();
        const response = await client.invoke<any>(invocation);
        if (!response.ok) {
          throw response.error;
        }
        return deserializeMap(response.value);
      }
      throw new Error('User declined invocation');
    default:
      throw new Error('Method not found.');
  }
};

2. Plugin Wrapper

Plugin wrappers are directly embedded into Polysnap with the Polywrap Client. These Plugin wrappers expose the capabilities provided inside Metamask’s SES environement Ex: HTTP, Ethereum JSON-RPC

We have added support for three different plugins in this MVP.

  1. HTTP Plugin - exposes API that allows making HTTP GET/POST requests directly from the Wasm Wrapper
  2. IPFS Plugin - exposes API to store, pin and retrieve files from the IPFS
  3. IPFS URI Resolver Plugin - exposes API to resolve deployed Wasm Wrappers from the IPFS

3. Wasm Wrapper

Wasm wrappers are the Wasm modules built and deployed to IPFS using the Polywrap toolchain. Since Wasm is a sandboxed enviroment, it can’t perform any IO unless the host exposes an API to facilitate it. because of this, any Wasm wrappers can be securely run inside the MetaMask Snap. Polysnap can’t run any host functions, like accessing internet, without the help of Plugin wrappers. You can check out all of the IPFS deployed wrappers at: wrappers.io

export async function getClient(): Promise<PolywrapClient> {
  const ipfsResolverWrapperResult = await ipfsResolverPlugin(
    {},
  ).createWrapper();
  // IPFS URI RESOLVER
  if (!ipfsResolverWrapperResult.ok) {
    throw ipfsResolverWrapperResult.error;
  }
  const ipfsUriResolver = new WrapperResolver(
    Uri.from('ens/ipfs-resolver.polywrap.eth'),
    ipfsResolverWrapperResult.value,
  );

  // IPFS RESOLVER
  const ipfsWrapperResult = await ipfsPlugin({}).createWrapper();
  if (!ipfsWrapperResult.ok) {
    throw ipfsWrapperResult.error;
  }
  const ipfsResolver = new WrapperResolver(
    Uri.from('ens/ipfs.polywrap.eth'),
    ipfsWrapperResult.value,
  );

  // HTTP RESOLVER
  const httpWrapperResult = await httpPlugin({}).createWrapper();
  if (!httpWrapperResult.ok) {
    throw httpWrapperResult.error;
  }
  const httpResolver = new WrapperResolver(
    Uri.from('ens/http.polywrap.eth'),
    httpWrapperResult.value,
  );

  // AGGREGATED RESOLVER
  const resolver = RecursiveResolver.from([
    httpResolver,
    ipfsResolver,
    ipfsUriResolver,
    new ExtendableUriResolver(),
  ]);

  return new PolywrapClient({
    interfaces: [
      {
        interface: new Uri('wrap://ens/uri-resolver.core.polywrap.eth'),
        implementations: [
          new Uri('wrap://ens/ipfs-resolver.polywrap.eth'),
        ],
      },
    ],
    envs: [
      {
        uri: new Uri('wrap://ens/ipfs.polywrap.eth'),
        env: {
          provider: defaultIpfsProviders[0],
          fallbackProviders: defaultIpfsProviders.slice(1),
        },
      },
    ],
    resolver,
  });
}

Polysnap Website

The Polysnap website is a simple demo site we created to showcase PolySnap in action. It has a button used to connect a MetaMask wallet and install the Snap, and a query dashboard that submits a JSON RPC call to the Polysnap and displays the result.

2Y2rfYy

To send an invocation to the Snap, a user provides three elements:

  • A uri that resolves to a Wasm wrapper (the hackathon project supports only IPFS URIs, but a future version could support ENS resolution as well)
  • The name of the method to invoke
  • A json object args that contains the arguments that will be passed to the invoked method

When the “Invoke” button is clicked, the website reads the values in these fields and forwards them to the Snap. The Snap opens a confirmation window to request approval of the invocation, ensuring the app does not make any invocations without the end-user’s consent.

invoke

If the invocation is approved, the Polywrap Client in the Snap handles the invocation and returns the result. The result is printed in a box below the buttons. Of course, in a real-world app, developers would typically set up invocations programmatically and use the result differently.

As its name suggests, Polysnap is one Snap with the power of many. With just Polysnap, developers can invoke any method in any Polywrap Wasm wrapper written in any Wasm-compilable language.

The wrappers are downloaded at runtime and cached. Since Wasm wrappers are sandboxed and stateless, Polysnap is adding an additional layer of security to the already-secure Snap environment.

And it’s all completely decentralized.

What are the next steps if you would implement this?

Here is the list of all the improvements that we envisioned for the next version of the Polysnap:

  • Enable support for more Plugin Wrappers such as: Cache, Concurrency, Ethereum, Near, Tezos, etc
  • Add permission-based security for access to each Polywrap plugin, ensuring that security can be managed by the end user.
  • Create a PolySnap installation website to showcase the Snap, host documentation, make installation easy for end-users
  • Add support for IPFS, ENS and HTTP Polywrap URI Resolvers
  • Support app developers submitting custom configurations for each invocation of the Polywrap Client configurable while invoking over JSON-RPC with MetaMask Snap
  • Support custom cache management solutions for wrappers cached by PolySnap

Can you tell us a little bit about yourself and your team?

We are both members of dOrg, a DAO of freelance software engineers and designers, and also developers at Polywrap.

  • Kris - I am a software engineer in Web3. My life was changed in 2020 when a friend introduced me to dOrg and the decentralized web. I soon joined the team at Polywrap to help build the next generation of SDK developer tools.
  • Niraj - I am a Web3 Developer at dOrg and currently working on the Polywrap project as a Core developer. I have been working as Web3 Developer for past 2 years and contributed to Badger, Opolis, Coordinape, and Llama before joining the Polywrap.

What opportunities do you see with MetaMask Snaps and the possibilities it unlocks for the Web3 space?

We think MetaMask Snaps has the potential to make MetaMask become a hub that will onboard millions of new users to web3. Both developer experience and security are paramount to the success of any plugin ecosystem. We believe that Polysnap can bring both to MetaMask Snaps by making it even more easy-to-use, extensible and secure.

Any advice you would like to share with developers keen to try MetaMask Snaps?

MetaMask Snaps allow extending capabilities of MetaMask by writing new extension MetaMask Snaps for your MetaMask wallet. So, it is a very powerful tool that can enable tons of use-cases like integrating different L1 wallets directly inside the MetaMask, Provide more insights for the transactions, etc. And we want to make Devx for enabling such use-cases even easier with Polysnap.

MetaMask Snaps are still new, but in just one weekend we saw teams build incredible MetaMask Snaps--even a Cosmos wallet that lives in MetaMask. Even if the MetaMask Snaps feature is still in a pre-production state, start early!

Or instead of writing a new Snap, you might consider writing a Polywrap Wasm wrapper in a language like Rust or AssemblyScript. You can then not only invoke it with Polysnap without having to write a new Snap of your own but also use it anywhere with the Polywrap client.

Building with MetaMask Snaps

To get started with MetaMask Snaps:

  1. Checkout the developer docs
  2. Install MetaMask Flask
  3. Check out a MetaMask Snaps guide
  4. Stay connected with us on Twitter, GitHub discussions, and Discord

Keep an eye out for our team at the next hackathon in an area near you! Happy BUIDLing ⚒️

Disclaimer: MetaMask Snaps are generally developed by third parties other the ConsenSys Software. Use of third-party-developed MetaMask Snaps is done at your own discretion and risk and with agreement that you will solely be responsible for any loss or damage that results from such activities. ConsenSys makes no express or implied warranty, whether oral or written, regarding any third-party-developed Snaps and disclaims all liability for third-party developed MetaMask Snaps. Use of blockchain-related software carries risks, and you assume them in full when using MetaMask Snaps.

Receive our Newsletter