跳转到内容

Aptos 钱包标准

钱包标准为不同类型的钱包之间的互操作性提供指南。这确保 dapp 开发者无需更改其应用程序来处理不同的钱包。该标准为所有 dapp 开发者提供统一接口,方便添加新钱包并为每个应用带来更多用户。这种互操作性允许用户选择他们想要的钱包,而无需担心应用是否支持他们的用例。

为确保 Aptos 钱包之间的互操作性,需要满足以下条件:

  1. 助记词 - 一组可用于派生账户私钥的单词
  2. dapp API - 钱包的入口点,支持访问由钱包管理的身份
  3. 密钥轮换 - 处理助记词相关关系和不同钱包中账户恢复的功能

助记词短语是一个多单词短语,可用于生成账户地址。我们建议每个账户使用一个助记词,以更好地处理密钥轮换。然而,一些来自其他链的钱包可能希望支持一个助记词对应多个账户。为支持这两种用例,Aptos 钱包标准使用 比特币改进提案 (BIP44) 来派生助记词到账户的路径。

Aptos 账户创建可以通过以下方式在各钱包之间得到支持:

  1. 生成助记词短语,例如使用 BIP39。
  2. 从该助记词短语获取主种子。
  3. 使用 BIP44 派生路径检索账户地址(例如 m/44'/637'/0'/0'/0'
/**
* 使用 bip44 路径和助记词创建新账户,
* @param path. (例如 m/44'/637'/0'/0'/0')
* 详细描述: {@link https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki}
* @param mnemonics.
* @returns AptosAccount
*/
static fromDerivePath(path: string, mnemonics: string): AptosAccount {
if (!AptosAccount.isValidPath(path)) {
throw new Error("Invalid derivation path");
}
const normalizeMnemonics = mnemonics
.trim()
.split(/\s+/)
.map((part) => part.toLowerCase())
.join(" ");
const { key } = derivePath(path, bytesToHex(bip39.mnemonicToSeedSync(normalizeMnemonics)));
return new AptosAccount(new Uint8Array(key));
}

支持一个助记词对应多账户的钱包

Section titled “支持一个助记词对应多账户的钱包”

不建议这样做,因为一个助记词对应多个账户的模式使得处理密钥轮换更加困难(一个账户的助记词改变了,但其他账户的没有)。然而,许多其他生态系统的钱包使用这种模式,并通过以下步骤生成账户:

  1. 生成助记词短语,例如使用 BIP39。
  2. 从该助记词短语获取主种子。
  3. 使用 BIP44 派生路径检索私钥(例如 m/44'/637'/i'/0'/0'),其中 i 是账户索引。
  1. 递增 i 直到找到用户想要导入的所有账户。
  • 注意:迭代应该有限制,如果在迭代过程中账户不存在,继续迭代一个常量 address_gap_limit(目前为 10),以查看是否有其他账户。如果找到账户,则继续正常迭代。

例如:

const gapLimit = 10;
let currentGap = 0;
for (let i = 0; currentGap < gapLimit; i += 1) {
const derivationPath = `m/44'/637'/${i}'/0'/0'`;
const account = fromDerivePath(derivationPath, mnemonic);
const response = account.getResources();
if (response.status !== 404) {
wallet.addAccount(account);
currentGap = 0;
} else {
currentGap += 1;
}
}

比账户创建更重要的是钱包和 dapp 之间如何通信。

遵循 AIP-62,钱包标准定义了钱包和应用程序交互的 API。

钱包必须实现 AptosWallet 接口,包含钱包提供者信息和功能特性:

class MyWallet implements AptosWallet {
url: string;
version: "1.0.0";
name: string;
icon:
| `data:image/svg+xml;base64,${string}`
| `data:image/webp;base64,${string}`
| `data:image/png;base64,${string}`
| `data:image/gif;base64,${string}`;
chains: AptosChain;
features: AptosFeatures;
accounts: readonly AptosWalletAccount[];
}

钱包必须实现 AptosWalletAccount 接口,表示已被 dapp 授权的账户。

enum AptosAccountVariant {
Ed25519,
MultiEd25519,
SingleKey,
MultiKey,
}
class AptosWalletAccount implements WalletAccount {
address: string;
publicKey: Uint8Array;
chains: AptosChain;
features: AptosFeatures;
variant: AptosAccountVariant;
label?: string;
icon?:
| `data:image/svg+xml;base64,${string}`
| `data:image/webp;base64,${string}`
| `data:image/png;base64,${string}`
| `data:image/gif;base64,${string}`
| undefined;
}

如果钱包是 Web 扩展钱包(即通过 Chrome 扩展商店安装),钱包必须使用 registerWallet 方法注册自己,以通知 dapp 它已准备好使用。

const myWallet = new MyWallet();
registerWallet(myWallet);

如果钱包实现了标准必需功能,则被视为有效的 Aptos 钱包。

钱包必须抛出 AptosWalletError。标准要求支持 UnauthorizedInternalError,但钱包可以抛出自定义的 AptosWalletError 错误。

if (error) {
throw new AptosWalletError(AptosWalletErrorCode.Unauthorized);
}
if (error) {
throw new AptosWalletError(
AptosWalletErrorCode.Unauthorized,
"自定义未授权消息"
);
}
if (error) {
throw new AptosWalletError(-32000, "无效输入");
}

如果由于某种原因,dapp 决定实现自定义钱包集成:

dapp 使用 getAptosWallets() 函数获取所有符合 Aptos 标准的钱包。

import { getAptosWallets } from "@aptos-labs/wallet-standard";
let { aptosWallets, on } = getAptosWallets();

在首次加载时,在 dapp 加载之前,它会获取到目前为止已注册的所有钱包。要在此之后继续获取所有注册的钱包,dapp 必须添加事件监听器来监听新注册的钱包,并接收一个取消订阅函数,稍后可用于移除监听器。

const removeRegisterListener = on("register", function () {
let { aptosWallets } = getAptosWallets();
});
const removeUnregisterListener = on("unregister", function () {
let { aptosWallets } = getAptosWallets();
});

dapp 现在有了事件监听器,因此可以立即看到新钱包,无需轮询或重新列出。即使 dapp 在任何钱包之前加载也能正常工作(它会初始化,看不到钱包,然后在钱包加载时看到它们)。

dapp 通过调用与所需操作对应的功能名称来发出钱包请求。例如,要使用 connect 功能:

const onConnect = () => {
this.wallet.features["aptos:connect"].connect();
};

密钥轮换目前尚未在任何钱包中实现。轮换密钥的映射已实现,但 SDK 集成正在进行中。

导入私钥的钱包需要执行以下操作:

  1. 派生认证密钥。
  2. 在链上的账户来源表中查找认证密钥。
  • 如果账户不存在,则为新账户。要使用的地址是认证密钥。
  • 如果账户存在,则为密钥已轮换的账户,要使用的地址将来自该表。