AA Snap: Democratizing Account Abstraction

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 MetMask Snaps being built today.

AA Snap

Snap Repo: https://github.com/a42io/AAsnap

Why did you build it?

AA Snap is built for democratizing Account Abstraction (AA) for all developers and users. We believe AA will be widely used in the future. However, currently, it takes work for devs to integrate it with their dapps and for users to use it.

From devs' perspective, it's necessary to write additional code that supports AA in addition to EOA.

From users' point of view, they cannot use their usual wallets, such as MetaMask, and need to install another wallet that supports AA, which is a bad UX.

Therefore, we wanted to make it easy to integrate and use AA by utilizing MetaMask Snaps.

Can you walk us through the technical implementation?

AA Snap is composed of three components: an example frontend, the MetaMask snap, and a temporary server. The snap enables devs and users to use an AA wallet (a contract wallet that supports EIP-4337) very easily. Please see the repository for the full implementation.

The example frontend

The example frontend is based on the template that is provided by MetaMask. It shows developers can easily integrate AA with their dapps by AA Snap.

For example, a balance of the AA wallet can be fetched with the simple code below.

const balance = (await window.ethereum.request({
    method: 'wallet_invokeSnap',
    params: [
        defaultSnapOrigin,
        {
            method: 'balance',
        },
    ],
})) as string;

To send a transaction from the AA wallet, you can just write the code below by passing a beneficiary address and value.

await window.ethereum.request({
    method: 'wallet_invokeSnap',
    params: [
        defaultSnapOrigin,
        {
            method: 'transfer',
            params: {
                to,        // beneficiary address
                value,     // value in ETH or MATIC
            },
        },
    ],
});

How easy is it to use the AA wallet from the user’s perspective? Try it here: https://AAsn.app

The Snap

The snap gets an instance of AA with the code below. To implement AA, we used the @account-abstraction/sdk npm package.

Since the AA wallet address can be deterministically derived from the user’s EOA, no user input is required.

export const getAbstractAccount = async (): Promise<SimpleAccountAPI> => {
    const provider = new ethers.providers.Web3Provider(wallet as any);
    await provider.send('eth_requestAccounts', []);
    const owner = provider.getSigner();
    const aa = new SimpleAccountAPI({
        provider,
        entryPointAddress,
        owner,
        factoryAddress,
    });
    return aa;
};

There are three steps to send a transaction from the AA wallet:

  1. The snap shows a confirmation within the MetaMask using the snap’s snap_confirm method. This process is not required but is needed for better UX because there’s no way for users to know the details of what they will sign.
  2. The code creates a user operation of the AA wallet and requests to sign a hash of the user operation. This process is almost the same as creating a tx from EOA and signing it.
  3. Send the signed user operation to a bundler via our server.
export const transfer = async (target: string, ethValue: string) => {
    const value = ethers.utils.parseEther(ethValue);
    const aa = await getAbstractAccount();
    const address = await aa.getAccountAddress();

    // 1. Show confirmation within MetaMask
    const result = await wallet.request({
        method: 'snap_confirm',
        params: [{
            prompt: 'Transfer',
            description: 'Transfer from your Abstraction Account',
            textAreaContent: `from: ${address}\ntarget: ${target}\nvalue: ${ethValue}`,
        }],
    });

    if (!result) {
        return;
    }

    // 2. Create a signed user operation of AA wallet
    const op = await aa.createSignedUserOp({
        target,
        value,
        data: '0x',
        maxFeePerGas,
        maxPriorityFeePerGas,
    });

    const printedOp = await printOp(op);
    const body = JSON.stringify({
        op: printedOp,
    });

    // 3. Send a signed user operation to the bundler
    const response = await fetch(SERVER_END_POINT {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body,
    });
};

The temporary server

During the hackathon, we encountered an error when calling the sendUserOpToBundler method inside the snap. Since we didn’t have time to dig into the issue, we decided to create a server to solve the problem temporarily. The temporary server just receives the signed user operation and passes it to a bundler.

Because of the error probably came from dependencies because there are some restrictions in the MetaMask Snaps execution environment. We think the temporary server can be removed by following this document: Patching Dependencies

app.post('/aa', async (req, res) => {
  const provider = new ethers.providers.JsonRpcProvider(
    process.env.RPC_ENDPOINT,
  );
  const client = await getHttpRpcClient(
    provider,
    process.env.BUNDLER_URL as string,
    process.env.ENTRY_POINT_ADDRESS as string,
  );

  try {
    const uoHash = await client.sendUserOpToBundler(JSON.parse(req.body.op));
    return res.json({
      uoHash,
    });
  } catch (e) {
    console.log(e);
    return res.status(400).send();
  }
});

What are the next steps if you would implement this?

We would like to remove unnecessary server-side code and run the snap alone. Also, currently, it is possible for the MetaMask Snaps developer like us to replace a transaction hash to be signed with a malicious one, so we would like to introduce or propose a mechanism to prevent it.

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

team

Our team is running a small startup - a42 - in Japan and Singapore. a42 is building a Web3 smart lock "wΞlock" that enables auth with smart contracts including NFT and ENS: https://a42.io/w3lock In 2022, we attended several hackathons to learn new technologies like MetaMask Snaps.

Hideyoshi Moriya (piyo.eth): Hideyoshi has been experimenting and building Ethereum based apps since 2017. He developed a collaborative NFT art project "Pixereum" in 2017. He worked at Google as a technical product specialist and started his company "a42" in 2019. In a42, he develops dapps that can work in the real world. He is currently developing a Web3 smart lock "wΞlock" that can be opened by ENS/NFT based on the PoC he developed in 2021: https://twitter.com/hm0429/status/1465241679800111107

Hiroyuki Tachibana (tachibana.eth): Hiroyuki is a co-founder and engineer at a42. He led a project to build a blockchain auth module for IoT devices "w3a.io" and received a grant from Ethereum Foundation. He received several prizes at ETHGlobal hackathons through 2022. He is currently developing a zkp privacy solution for the Web3 smart lock product.

Yusuke Moriya (yskmyskm.eth): Yusuke is a designer at a42. He worked as CG / VFX artist in various video productions and was involved in global brands to small-scale music videos. He has 12 years of experience in the film industry and specializes in abstract expressions using computer graphics. He has also worked as a cinematographer and documentary director.

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

We believe that MetaMask Snaps will make complex Web3 technologies easier for both developers and users and will significantly help traditional Web applications integrate with Web3. We are as excited about creating MetaMask Snaps as we were when the first mobile app store came out. We encourage you to give it a try and realize your ideas!

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

We recommend starting with the MetaMask Snaps template (https://github.com/MetaMask/template-snap-monorepo) and modifying it first.

Tip: You may be wondering how to get the injected ethereum object. The wallet object is equivalent to it.

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 MetaMask Snaps and disclaims all liability for third-party developed Snaps. Use of blockchain-related software carries risks, and you assume them in full when using MetaMask Snaps.

Receive our Newsletter