MetaMask Snaps é o roteiro para tornar a MetaMask a carteira mais extensível do mundo. Como desenvolvedor, você pode trazer seus recursos e APIs para a MetaMask de formas totalmente novas. Os desenvolvedores Web3 são o núcleo desse crescimento, e esta série tem como objetivo destacar os MetaMask Snaps inovadores que estão sendo criados hoje.
Aleph Zero Snap com Piotr e Michael
Conversamos com os desenvolvedores Web3 Piotr e Michael, que criaram o Aleph Zero Snap no ETHWarsaw Hackathon.
Repositório do Snap: https://github.com/piotr-roslaniec/ethwarsaw-2022/tree/main/azero-snap/packages/snap-adapter
Por que vocês o criaram?
Na nossa visão, a experiência de carteira no ecossistema Polkadot ainda deixa a desejar. Por outro lado, a integração com o MetaMask Snaps poderia ajudar a integrar uma população já existente de usuários web3 à rede Aleph Zero.
Podem nos guiar pela implementação técnica?
O Adapter
Nosso snap é composto por duas partes. A primeira é um site com o qual os usuários interagem. A segunda é o Snap em si. Esses dois elementos funcionam de forma semelhante à arquitetura cliente-servidor, onde o snap é o servidor e o site é o cliente. Para refletir essa divisão natural, extraímos a API do snap para um pacote separado chamado adapter. Ele expõe todos os métodos possíveis que o site pode usar para interagir com o snap. Essa camada de abstração facilita o uso do Snap por outros clientes (sites). Nossa API pode ser descrita por quatro métodos, cujos nomes são bastante autoexplicativos. Se quiser se aprofundar, aqui está o código completo.
export const getAccountFromSeed = async (seed: string): Promise<PublicAccount> => {
return await requestSnap("getAccountFromSeed", [seed]);
}
export const generateNewAccount = async (): Promise<PublicAccount> => {
return await requestSnap("generateNewAccount", []);
}
export const signTransaction = async (transaction: SignerPayloadJSON): Promise<{ signature: string }> => {
return await requestSnap("signTransaction", [transaction as unknown as string]);
}
export const getAccounts = async (): Promise<string[]> => {
return await requestSnap("getAccounts", []);
}
O app
Então, explicamos a conexão entre nossas camadas — vamos avançar para a primeira camada: o site. Fizemos um fork do polkadot app oficial baseado em navegador. Em seguida, precisamos substituir funcionalidades essenciais como assinar transações, criar contas ou recuperá-las, da carteira baseada em navegador para a baseada no Snap. Por exemplo, para assinar transações dentro da MetaMask, alteramos a implementação do QrSigner para usar o signTransaction do Snap.
A integração do MetaMask Snap com o polkadot app trouxe duas melhorias:
Primeiramente, permitiu herdar o modelo de segurança da extensão MetaMask em vez de usar a carteira do polkadot app, que é baseada em armazenamento local.
Em segundo lugar, pudemos usar a seed da MetaMask para criar um polkadot app de forma determinística, de modo que sempre que exportarmos nossa seed, a chave Aleph Zero também será exportada.
A API da MetaMask
A MetaMask fornece chamadas de API que podem ser usadas pelos MetaMask Snaps, se permitido pelo usuário. Neste caso, utilizamos duas delas:
snap_manageState — que nos permite armazenar dados criptografados dentro da MetaMask. Por exemplo, usamos para armazenar as contas criadas. Interagir com o estado é muito simples e pode ser resumido a:
const request = async (method: 'clear' | 'get' | 'update', newState?: unknown): Promise<any> => {
if (['get', 'clear'].includes(method) && newState) {
throw new Error(`Cannot ${method} with newState`);
}
if (method == 'update' && !newState) {
throw new Error('Cannot call update without newState');
}
const params = newState ? [method, newState] : [method];
return await wallet.request({
method: `snap_manageState`,
params,
});
}
snap_getBip44Entropy_ — para obter entropia da seed da MetaMask. docs
Vale mencionar que existe uma biblioteca MetaMask muito interessante chamada passworder, que nos permite criptografar e descriptografar o estado com facilidade. No nosso caso, usamos novamente a entropia da seed da MetaMask como chave simétrica para criptografar o estado.
O SDK
Então, recapitulando: hackeamos o polkadot app injetando algum javascript aqui e ali, habilitando o uso do Snap. O Snap pode obter entropia da seed dentro da MetaMask e gerenciar o estado criptografado. Portanto, o último passo seria integrar com o SDK do Aleph Zero — polkadotjs.
Isso parece uma tarefa simples: basta executar npm install @polkadot/api e importá-lo. No entanto, importar dependências para o Snap pode ser desafiador. Essa dificuldade surge porque o Snap não é avaliado diretamente na máquina virtual JavaScript — ele precisa primeiro ser isolado no Secure ECMAScript (SES). Em resumo, um código que normalmente compila sem problemas pode falhar ao ser carregado no SES.
Código
Ao trabalhar com a maioria das bibliotecas, não teremos problemas, mas não foi o nosso caso. Tivemos dois problemas ao avaliar o polkadotjs SDK dentro do SES; para contorná-los, escrevemos dois scripts que eram executados no código-fonte empacotado.
O primeiro foi causado por código wasm formatado incorretamente embutido dentro de um arquivo JavaScript. Aqui está o script de correção:
const fs = require('fs').promises;
const path = require('path');
const dirPath =
'../../../../node_modules/@substrate/smoldot-light/dist/cjs/instance/autogen';
async function fixDoubleSlash() {
const files = ['wasm0.js', 'wasm1.js', 'wasm2.js'];
for (const file of files) {
console.log('Fixing ${file}');
const filePath = path.join(__dirname, dirPath, file);
const contents = await fs.readFile(filePath, 'utf8');
let fixedContents = contents;
while (fixedContents.includes('//')) {
fixedContents = fixedContents.split('//').join('/"+"/');
}
await fs.writeFile(filePath, fixedContents);
}
}
fixDoubleSlash();
O segundo problema também foi causado pela extensão polkadot, que tentava acessar o método window.addEventListener, que é proibido no SES. Também foi resolvido de forma trivial com um simples
const fs = require('fs').promises;
const path = require('path');
const bundlePath = path.join(__dirname, '../dist/bundle.js');
async function fix() {
console.log(`Fixing ${bundlePath}`);
const contents = await fs.readFile(bundlePath, 'utf8');
const replaceMissingTypes = '\n\nwindow.addEventListener = function(){};\n\n';
const fixed = contents.replace('\n\n', replaceMissingTypes);
await fs.writeFile(bundlePath, fixed);
}
fix();
Quais são os próximos passos se vocês fossem implementar isso?
Adoraríamos tornar nosso snap totalmente compatível com o polkadot.js e viável como uma carteira web no ecossistema Polkadot.
Podem nos contar um pouco sobre vocês e sua equipe?
Michael é programador em um banco polonês e trabalha diariamente com tecnologia blockchain. Ele adora programar e aprender coisas novas.
Piotr é engenheiro de software na NuCypher. Ele se interessa por privacidade, dados e sistemas distribuídos.
Quando vocês conheceram o MetaMask Snaps pela primeira vez e como foi essa experiência?
Piotr — Inicialmente soube sobre o MetaMask Snaps durante a Devcon em Osaka. O assunto ressurgiu no início deste ano, pouco antes do hackathon EthDenver.
Michaeł — Piotr me apresentou ao MetaMask Snaps, logo após o EthDenver.
O que diferencia o MetaMask Snaps de outras carteiras?
A capacidade de acessar criptografia/chaves privadas específicas de aplicativos no seu dapp — isso permite criar novos fluxos de usuário sobre uma experiência familiar de carteira no navegador.
Como é para vocês e sua equipe criar MetaMask Snaps com a MetaMask?
O básico está lá, mas criar MetaMask Snaps mais sofisticados, especialmente os que exigem elementos de UI personalizados, é complicado. Passamos muito tempo tentando encaixar nosso modelo mental de experiência de desenvolvedor web nos Snaps — coisas como deploy, testes, etc.
O que o MetaMask Snaps significa para vocês?
É uma ferramenta que dá poder real à comunidade: cada um decide por si mesmo quais recursos sua MetaMask deve ter. Os plugins permitem democratizar o desenvolvimento e possibilitar um crescimento mais rápido.
Que oportunidades vocês enxergam no MetaMask Snaps e nas possibilidades que ele desbloqueia para o espaço Web3?
Acesso a mais redes (especialmente as não-EVM); a capacidade de intervir no ciclo de vida das transações e adicionar lógica personalizada (como verificações de segurança) ou personalizar a UX; armazenamento criptografado no navegador para conteúdo sensível específico de dapps, e também a capacidade de "trazer sua própria criptografia" para seus usuários com o MetaMask Snaps.
Algum conselho que gostariam de compartilhar com desenvolvedores interessados em experimentar o MetaMask Snaps?
Compilar bibliotecas JS no SES pode ser difícil, mas pela nossa experiência não é impossível. O ciclo de feedback no desenvolvimento com MetaMask é bastante longo, então certifique-se de escrever testes unitários.
Existe uma comunidade de desenvolvedores pequena, mas ativa no MetaMask Snaps — visitar o Discord, as discussões no GitHub etc. pode ser muito recompensador, especialmente porque o produto ainda é novo e há pouco material educativo disponível fora dessa bolha.
Criando com MetaMask Snaps
Para começar com o MetaMask Snaps:
Confira a documentação para desenvolvedores
Instale o MetaMask Flask
Confira um guia do MetaMask Snaps
Fique conectado conosco no Twitter, nas discussões do GitHub e no Discord
Fique de olho na nossa equipe no próximo hackathon perto de você! Bom BUIDLing ⚒️
Aviso legal: Os MetaMask Snaps são geralmente desenvolvidos por terceiros que não a Consensys Software. O uso de MetaMask Snaps desenvolvidos por terceiros é feito a seu próprio critério e risco, e com o entendimento de que você será o único responsável por qualquer perda ou dano resultante de tais atividades. A Consensys não oferece nenhuma garantia expressa ou implícita, seja verbal ou escrita, em relação a MetaMask Snaps desenvolvidos por terceiros, e se isenta de toda responsabilidade por MetaMask Snaps desenvolvidos por terceiros. O uso de software relacionado a blockchain envolve riscos, e você os assume integralmente ao usar o MetaMask Snaps.