MetaMask Snaps 是讓 MetaMask 成為全球最具擴展性錢包的發展藍圖。身為開發者,您可以透過全新方式將您的功能與 API 帶入 MetaMask。Web3 開發者是這項成長的核心,本系列旨在展示當今正在建構的創新 MetaMask Snaps。
Aleph Zero Snap:與 Piotr 和 Michael 的對談
我們與 Web3 開發者 Piotr 和 Michael 進行了交流,他們在 ETHWarsaw Hackathon 上建構了 Aleph Zero Snap。
Snap 儲存庫: https://github.com/piotr-roslaniec/ethwarsaw-2022/tree/main/azero-snap/packages/snap-adapter
為什麼要建構它?
在我們看來,Polkadot 生態系統中的錢包使用體驗仍有不足之處。另一方面,與 MetaMask Snaps 整合有助於將現有的 Web3 用戶引導至 Aleph Zero 網路。
能否帶我們了解技術實作的細節?
Adapter(轉接層)
我們的 Snap 由兩個部分組成。第一部分是使用者互動的網站,第二部分是實際的 Snap 本體。這兩個元素的運作方式類似於客戶端—伺服器架構,其中 Snap 扮演伺服器角色,網站則是客戶端。為了反映這個自然的邊界,我們將 Snap API 提取至一個名為 adapter 的獨立套件。它公開了網站與 Snap 互動時可能使用的所有方法。這樣的抽象層讓其他客戶端(網站)更容易使用 Snap。我們的 API 可由四個方法描述,其名稱相當一目了然。如果您想深入了解,請參閱完整的程式碼。
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", []);
}
應用程式
我們已說明各層之間的連接方式,現在讓我們進入第一層——網站。我們 fork 了官方的瀏覽器版 polkadot app,接著需要將核心功能(如簽署交易、建立帳戶或還原帳戶)從瀏覽器內建錢包替換為基於 Snap 的版本。例如,為了在 MetaMask 內簽署交易,我們將 QrSigner 的實作改為使用 Snap 的 signTransaction。
將 MetaMask Snap 與 polkadot app 整合帶來了兩項改進:
首先,它讓我們能夠繼承 MetaMask 擴充功能的安全模型,而非使用基於本機儲存的 polkadot app 錢包。
其次,我們可以使用 MetaMask 助記詞以確定性方式建立 polkadot app,因此每當我們匯出助記詞時,Aleph Zero 金鑰也會一併匯出。
MetaMask API
MetaMask 提供 API 呼叫,在用戶授權的情況下可供 MetaMask Snaps 使用。在此案例中,我們使用了其中兩個:
snap_manageState——允許我們在 MetaMask 內儲存加密資料。例如,我們用它來儲存已建立的帳戶。與狀態互動非常簡單,可簡化為:
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_——用於從 MetaMask 助記詞取得熵值。文件
值得一提的是,MetaMask 有一個非常實用的函式庫 passworder,讓我們能輕鬆加密和解密狀態。在我們的案例中,我們再次使用來自 MetaMask 助記詞的熵值作為加密狀態的對稱金鑰。
SDK
讓我們來回顧一下:我們透過在各處注入 JavaScript 的方式修改了 polkadot app,使其能夠使用 Snap。Snap 可以從 MetaMask 內的助記詞取得熵值並管理加密狀態。最後一步是整合 Aleph Zero 的 SDK——polkadotjs。
這看起來是個簡單的任務,只需執行 npm install @polkadot/api 並匯入即可。然而,將相依套件匯入 Snap 可能頗具挑戰性。這個困難的出現是因為 Snap 並非直接在 JavaScript 虛擬機器上執行,它必須先在 Secure ECMAScript(SES)中進行沙箱化。簡而言之,通常能正常編譯的程式碼,在 SES 中載入時可能會失敗。
程式碼
在使用大多數函式庫時不會遇到問題,但我們的情況並非如此。我們在 SES 中執行 polkadotjs SDK 時遇到了兩個問題,為了解決這些問題,我們編寫了兩個腳本,在打包後的原始碼上執行。
第一個問題是由嵌入在 JavaScript 檔案中格式不正確的 wasm 程式碼所引起。以下是修復腳本:
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();
第二個問題同樣由 polkadot 擴充功能引起,它嘗試存取在 SES 中被禁止的 window.addEventListener 方法。這個問題也透過簡單的方式輕鬆解決:
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();
如果要繼續實作,下一步是什麼?
我們希望讓我們的 Snap 與 polkadot.js 完全相容,並成為 Polkadot 生態系統中可行的網頁錢包。
能否介紹一下您自己和您的團隊?
Michael 是一位在波蘭銀行工作的程式設計師,日常工作涉及區塊鏈技術。他熱愛程式設計並持續學習新知識。
Piotr 是一位在 NuCypher 工作的軟體工程師,對隱私、資料和分散式系統深感興趣。
您是何時首次接觸 MetaMask Snaps 的,體驗如何?
Piotr——最初是在大阪的 Devcon 上了解到 MetaMask Snaps。這個話題在今年稍早、EthDenver Hackathon 前不久再度浮現。
Michaeł——Piotr 在 EthDenver 結束後不久向我介紹了 MetaMask Snaps。
MetaMask Snaps 與其他錢包有何不同?
它能夠在您的 dapp 中存取應用程式專屬的加密技術/私鑰——這讓開發者得以在熟悉的瀏覽器錢包體驗之上建構全新的用戶流程。
請分享您和團隊使用 MetaMask 建構 MetaMask Snaps 的體驗?
基礎功能已具備,但建構更複雜的 MetaMask Snaps,尤其是需要自訂 UI 元素的 Snaps,仍相當棘手。我們花了大量時間思考如何將我們對 Web 開發者體驗的理解融入 Snaps 的開發中,例如部署、測試等環節。
MetaMask Snaps 對您而言意味著什麼?
它是一個賦予社群真正力量的工具,每個人都能自行決定他們的 MetaMask 應具備哪些功能。插件讓開發民主化,並促進更快速的成長。
您認為 MetaMask Snaps 為 Web3 領域帶來了哪些機會與可能性?
能夠存取更多網路(尤其是非 EVM 網路);能夠介入交易生命週期並加入自訂邏輯(如安全性檢查)或自訂使用者體驗;在瀏覽器中為 dapp 專屬的敏感內容提供加密儲存,以及透過 MetaMask Snaps 為用戶「帶入自訂加密技術」的能力。
對於有意嘗試 MetaMask Snaps 的開發者,您有什麼建議?
在 SES 中編譯 JS 函式庫可能相當困難,但根據我們的經驗,並非不可能。MetaMask 的開發回饋週期相當長,因此請務必撰寫單元測試。
MetaMask Snaps 有一個規模雖小但相當活躍的開發者社群——參與 Discord、GitHub 討論等活動非常值得,尤其是這個產品仍相當新穎,社群外部的教學資源目前還相當有限。
使用 MetaMask Snaps 進行建構
開始使用 MetaMask Snaps:
查閱開發者文件
安裝 MetaMask Flask
參考 MetaMask Snaps 指南
透過 Twitter、GitHub 討論區和 Discord 與我們保持聯繫
請留意我們的團隊將出現在您附近的下一場 Hackathon!祝 BUIDL 愉快 ⚒️
免責聲明:MetaMask Snaps 通常由 Consensys Software 以外的第三方開發。使用第三方開發的 MetaMask Snaps 須由您自行判斷並承擔風險,且您同意對因此類活動所造成的任何損失或損害負全部責任。Consensys 對第三方開發的 MetaMask Snaps 不作任何明示或暗示的保證,並對第三方開發的 MetaMask Snaps 免除一切責任。使用區塊鏈相關軟體存在風險,使用 MetaMask Snaps 即表示您完全承擔這些風險。