MetaMask Snaps ist der Fahrplan, um MetaMask zur erweiterbarsten Wallet der Welt zu machen. Als Entwickler kannst du MetaMask auf völlig neue Weise mit deinen Features und APIs bereichern. Web3-Entwickler sind der Kern dieses Wachstums, und diese Serie soll die innovativen MetaMask Snaps vorstellen, die heute entwickelt werden.
Aleph Zero Snap mit Piotr und Michael
Wir sprechen mit den Web3-Entwicklern Piotr und Michael, die den Aleph Zero Snap beim ETHWarsaw Hackathon entwickelt haben.
Snap Repo: https://github.com/piotr-roslaniec/ethwarsaw-2022/tree/main/azero-snap/packages/snap-adapter
Warum habt ihr es entwickelt?
Unserer Meinung nach lässt die Wallet-Erfahrung im Polkadot-Ökosystem noch zu wünschen übrig. Andererseits könnte die Integration mit MetaMask Snaps dabei helfen, eine bestehende Web3-Nutzerbasis in das Aleph Zero-Netzwerk einzuführen.
Könnt ihr uns durch die technische Umsetzung führen?
Der Adapter
Unser Snap besteht aus zwei Teilen. Der erste ist eine Website, mit der Nutzer interagieren. Der zweite ist der eigentliche Snap. Diese beiden Elemente funktionieren ähnlich wie eine Client-Server-Architektur, wobei der Snap der Server und die Website der Client ist. Um diese natürliche Grenze widerzuspiegeln, haben wir die Snap-API in ein separates Paket namens adapter ausgelagert. Es stellt alle möglichen Methoden bereit, die die Website zur Interaktion mit dem Snap verwenden kann. Eine solche Abstraktionsschicht erleichtert die Nutzung des Snaps durch andere Clients (Websites). Unsere API lässt sich durch vier Methoden beschreiben, deren Namen ziemlich selbsterklärend sind. Wer tiefer eintauchen möchte, findet hier den vollständigen Code.
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", []);
}
Die App
Nachdem wir die Verbindung zwischen unseren Schichten erklärt haben, kommen wir zur ersten Schicht – der Website. Wir haben die offizielle browserbasierte Polkadot-App geforkt. Anschließend mussten wir zentrale Funktionen wie das Signieren von Transaktionen, das Erstellen von Konten oder deren Wiederherstellung von der browserbasierten Wallet auf die Snap-basierte Lösung umstellen. Um beispielsweise Transaktionen innerhalb von MetaMask zu signieren, haben wir die Implementierung von QrSigner so geändert, dass stattdessen signTransaction des Snaps verwendet wird.
Die Integration von MetaMask Snap mit der Polkadot-App brachte zwei Verbesserungen mit sich:
Erstens ermöglichte sie es, das Sicherheitsmodell der MetaMask-Extension zu übernehmen, anstatt die Polkadot-App-Wallet zu verwenden, die auf lokalem Speicher basiert.
Zweitens konnten wir den MetaMask-Seed nutzen, um eine Polkadot-App auf deterministische Weise zu erstellen – wann immer wir unseren Seed exportieren, wird der Aleph Zero-Schlüssel ebenfalls exportiert.
Die MetaMask-API
MetaMask stellt API-Aufrufe bereit, die von MetaMask Snaps genutzt werden können, sofern der Nutzer dies erlaubt. In diesem Fall haben wir zwei davon verwendet:
snap_manageState – ermöglicht es uns, verschlüsselte Daten innerhalb von MetaMask zu speichern. Wir nutzen es beispielsweise, um erstellte Konten zu speichern. Die Interaktion mit dem State ist sehr einfach und lässt sich vereinfacht so darstellen:
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_ – um Entropie aus dem MetaMask-Seed zu beziehen. Docs
Erwähnenswert ist auch die sehr nützliche MetaMask-Bibliothek passworder, die es uns ermöglicht, den State einfach zu ver- und entschlüsseln. In unserem Fall haben wir erneut die Entropie aus dem MetaMask-Seed als symmetrischen Schlüssel zur Verschlüsselung des States verwendet.
Das SDK
Fassen wir zusammen: Wir haben die Polkadot-App modifiziert, indem wir hier und da JavaScript eingefügt haben, um die Nutzung des Snaps zu ermöglichen. Der Snap kann Entropie aus dem Seed innerhalb von MetaMask beziehen und verschlüsselten State verwalten. Der letzte Schritt wäre also die Integration mit dem SDK für Aleph Zero – polkadotjs.
Das klingt nach einer einfachen Aufgabe: einfach npm install @polkadot/api ausführen und importieren. Das Importieren von Abhängigkeiten in einen Snap kann jedoch eine Herausforderung sein. Diese Schwierigkeiten entstehen, weil der Snap nicht direkt auf der JavaScript Virtual Machine ausgeführt wird – er muss zunächst in Secure ECMAScript (SES) sandboxed werden. Kurz gesagt: Code, der normalerweise problemlos kompiliert, kann beim Laden in SES fehlschlagen.
Code
Bei den meisten Bibliotheken treten keine Probleme auf, aber bei uns war das nicht der Fall. Wir hatten zwei Probleme bei der Auswertung des polkadotjs SDK innerhalb von SES. Um diese zu beheben, haben wir zwei Skripte geschrieben, die auf dem gebündelten Quellcode ausgeführt wurden.
Das erste Problem wurde durch falsch formatierten WASM-Code verursacht, der in eine JavaScript-Datei eingebettet war. Hier ist das Korrekturskript:
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();
Das zweite Problem wurde ebenfalls durch die Polkadot-Extension verursacht, die versuchte, auf die Methode window.addEventListener zuzugreifen, was in SES nicht erlaubt ist. Es wurde ebenfalls trivial gelöst mit einem einfachen
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();
Was sind die nächsten Schritte, wenn ihr das weiterentwickeln würdet?
Wir würden unseren Snap gerne vollständig kompatibel mit polkadot.js machen und ihn als Web-Wallet im Polkadot-Ökosystem etablieren.
Könnt ihr uns ein wenig über euch und euer Team erzählen?
Michael ist Programmierer bei einer polnischen Bank und arbeitet täglich mit Blockchain-Technologie. Er liebt Programmieren und das Erlernen neuer Dinge.
Piotr ist Software-Engineer bei NuCypher. Er interessiert sich für Datenschutz, Daten und verteilte Systeme.
Wann seid ihr zum ersten Mal mit MetaMask Snaps in Berührung gekommen und wie war eure Erfahrung?
Piotr – Ich habe zum ersten Mal während der Devcon in Osaka von MetaMask Snaps erfahren. Das Thema tauchte Anfang dieses Jahres wieder auf, kurz vor dem EthDenver Hackathon.
Michaeł – Piotr hat mich kurz nach EthDenver mit MetaMask Snaps bekannt gemacht.
Was unterscheidet MetaMask Snaps von anderen Wallets?
Die Möglichkeit, auf anwendungsspezifische Kryptografie/Private Keys in deiner dApp zuzugreifen – das ermöglicht die Entwicklung neuer User Flows auf Basis einer vertrauten Browser-Wallet-Erfahrung.
Erzählt uns, wie es für euch und euer Team ist, MetaMask Snaps mit MetaMask zu entwickeln?
Die Grundlagen sind vorhanden, aber die Entwicklung komplexerer MetaMask Snaps – insbesondere solcher, die benutzerdefinierte UI-Elemente erfordern – ist knifflig. Wir haben viel Zeit damit verbracht herauszufinden, wie wir unser mentales Modell der Web-Entwicklererfahrung in Snaps übertragen können – Dinge wie Deployment, Testing usw.
Was bedeutet MetaMask Snaps für euch?
Es ist ein Werkzeug, das der Community echte Macht gibt – jeder entscheidet selbst, welche Features seine MetaMask haben soll. Plugins ermöglichen es, die Entwicklung zu demokratisieren und schnelleres Wachstum zu fördern.
Welche Möglichkeiten seht ihr bei MetaMask Snaps und den Chancen, die es für den Web3-Bereich eröffnet?
Zugang zu mehr Netzwerken (insbesondere nicht-EVM-basierten); die Möglichkeit, in den Transaktionslebenszyklus einzugreifen und benutzerdefinierte Logik hinzuzufügen (wie Sicherheitsprüfungen) oder die UX anzupassen; verschlüsselter Speicher im Browser für dApp-spezifische sensible Inhalte sowie die Möglichkeit, deinen Nutzern mit MetaMask Snaps „eigene Kryptografie mitzubringen".
Habt ihr Ratschläge für Entwickler, die MetaMask Snaps ausprobieren möchten?
Das Kompilieren von JS-Bibliotheken in SES kann schwierig sein, aber aus unserer Erfahrung ist es nicht unmöglich. Der Feedback-Zyklus bei der Entwicklung mit MetaMask ist recht lang – schreibe daher unbedingt Unit-Tests.
Es gibt eine kleine, aber aktive Entwickler-Community rund um MetaMask Snaps – Discord, GitHub-Diskussionen usw. zu besuchen kann sehr lohnenswert sein, besonders da das Produkt noch neu ist und außerhalb dieser Community nur wenig Lernmaterial verfügbar ist.
Mit MetaMask Snaps entwickeln
So startest du mit MetaMask Snaps:
Schau dir die Entwicklerdokumentation an
Installiere MetaMask Flask
Sieh dir einen MetaMask Snaps-Leitfaden an
Bleib mit uns in Kontakt auf Twitter, GitHub-Diskussionen und Discord
Halte bei unserem Team Ausschau beim nächsten Hackathon in deiner Nähe! Happy BUIDLing ⚒️
Haftungsausschluss: MetaMask Snaps werden in der Regel von Drittanbietern entwickelt, die nicht zu Consensys Software gehören. Die Nutzung von durch Dritte entwickelten MetaMask Snaps erfolgt nach eigenem Ermessen und auf eigenes Risiko, mit dem Einverständnis, dass du allein für etwaige Verluste oder Schäden verantwortlich bist, die aus solchen Aktivitäten entstehen. Consensys gibt keine ausdrücklichen oder stillschweigenden Garantien, weder mündlich noch schriftlich, in Bezug auf durch Dritte entwickelte MetaMask Snaps und lehnt jede Haftung für durch Dritte entwickelte MetaMask Snaps ab. Die Nutzung von Blockchain-bezogener Software ist mit Risiken verbunden, die du bei der Verwendung von MetaMask Snaps vollständig selbst trägst.