# 钱包与账户

<div data-with-frame="true"><figure><img src="https://629346125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FH74xqoobupBWwBsVMJhK%2Fuploads%2Fgit-blob-2d782448481ac5b6f078b0307da62a30af1d8d6f%2FFrame%20316126255.jpg?alt=media" alt=""><figcaption></figcaption></figure></div>

{% hint style="info" %}
在 Stacks 中，“address”和“principal”这两个术语可以互换使用。
{% endhint %}

## 简介

Stacks 钱包是用于存储加密货币、NFT 及其他数字资产的软件或硬件工具。它们还用于在去中心化应用（dApp）中建立链上身份。这些钱包通过一个区块链地址以加密方式存储和管理每个用户的身份和资金，这一过程利用了公钥密码学。

### Stacks 钱包的用途

* 建立用户身份
* 存储资产
* 显示余额
* 签署交易
* 签署消息
* 参与比特币经济

Stacks 中的钱包由账户组成，采用的是账户模型，而不是像比特币那样的 UTXO 模型。这个模型比 UTXO 模型更简单，并且具有更传统的“余额”概念，类似于你在银行中会遇到的情况。在这个模型中，每个地址对某一种代币都有一个单独的“余额”数值，当交易发送到或从该账户发出时，这个数值会增加/减少。这也是大多数 Web3 生态系统所采用的方式。在 UTXO 模型中，网络作为账本运行，每个 UTXO 类似于一张现金钞票。

### Stacks 账户的组成部分

<div data-with-frame="true"><figure><img src="https://629346125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FH74xqoobupBWwBsVMJhK%2Fuploads%2FLK9iA5RYSuLt1pt6kcqL%2Fstacks-address-generation-flowchart.png?alt=media&#x26;token=f322e0cc-7d6d-4df6-a8d1-7cf08a8828ec" alt=""><figcaption></figcaption></figure></div>

* **私钥** - 私钥是一段字母数字代码，与单个公钥按 1:1 的方式配对。切勿将你的私钥分享给任何人。私钥用于证明你对某个公钥的所有权，也用于花费该特定密钥对所持有的资产。\
  \
  Stacks 中的私钥示例（32 字节，末尾附加 0x01 字节）：\
  `5a4133fec2cf923d37238d3ba2fcd2ee9c8dce882c22218fd210d8a02ceb2c7401`
* **公钥** - 公钥是从私钥通过数学方式推导出来的。它可以安全地共享，网络会使用它来验证由私钥生成的签名，而不会泄露私钥本身。\
  \
  Stacks 中的公钥示例（压缩格式）：\
  `02e8eb87862945d369511fdcce326ffef9a01b68c7d070e3ce685a5cbb9b1ecfc5`
* **地址（Principal）** - 地址是从公钥派生出的更短、更易于用户使用的表示形式。你可以分享它来在 Stacks 上接收 sBTC、STX、代币或 NFT，它还充当用户的链上标识符。\
  \
  Stacks 中的公共地址示例（c32check 编码）：\
  `SPM9G3CNGSCTB4956290NESM0MR9W9CCEPVEPSQC`

{% hint style="info" %}
私钥/公钥的生成使用加密曲线 **secp256k1** 。

Stacks 中使用的密码学签名算法是 **ECDSA** 基于 **secp256k1**.

地址是通过生成 **RIPEMD-160** 哈希值而创建的，该哈希来自公钥的 **SHA256** 。BTC 地址使用 **Base58Check**进行编码。对于 Stacks 地址，使用 [**c32check**](https://github.com/stacks-network/c32check) 。
{% endhint %}

Stacks 账户是拥有资产的实体，例如 Stacks（STX）代币。一个账户拥有地址、私钥、nonce 以及一种或多种资产余额。没有账户所有者的操作，资产不能离开账户。对资产（以及账户余额）的所有变更都需要相应的交易。

所有 Stacks 钱包也支持比特币地址，从而能够在 Stacks 和比特币两个生态系统之间无缝参与。

#### 地址格式 <a href="#address-formats" id="address-formats"></a>

Stacks 地址使用不同的前缀来表示它们所属的网络，这使得区分主网和测试网地址变得很容易。

```typescript
// 主网地址以 'SP' 开头
const mainnetAddress = 'SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159';

// 主网多签地址以 'SM' 开头
const multisigMainnetAddress = 'SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4'

// 测试网多签地址以 'SN' 开头
const multisigTestnetAddress = 'SNJSPGVBFZHPXGESC9ZQWVFSNF4RHNNRTW2HYYJ7'

// 测试网地址以 'ST' 开头
const testnetAddress = 'ST2F4BK4GZH6YFBNHYDDGN4T1RKBA7DA1BJZPJEJJ';
```

***

## 如何创建一个 Stacks 账户？

钱包的账户由符合 BIP39 标准的 24 词助记短语生成。这通常被称为 **助记词**。助记词可用于访问 Stacks 账户。

{% hint style="danger" %}
如果助记词丢失，与之关联的账户将无法恢复。任何个人或组织都无法找回丢失的助记词。
{% endhint %}

无论是开发者还是非开发者，都有几种不同的方法来创建 Stacks 账户。

{% stepper %}
{% step %}

#### 使用 Stacks.js

对于程序化生成钱包或从备份恢复账户时，你可以从助记种子短语派生地址。每个钱包可以包含多个账户，这些账户都通过不同的派生路径从同一个种子短语派生而来。

{% code expandable="true" %}

```typescript
import { generateWallet, generateSecretKey, type Wallet, Account } from '@stacks/wallet-sdk';
import { privateKeyToAddress, privateKeyToPublic } from '@stacks/transactions'

// 生成新的 24 词种子短语
const secretKey: string = generateSecretKey();

// 或使用已有的种子短语
// const secretKey = '已有的种子短语 ...';

const wallet: Wallet = await generateWallet({
  secretKey,
  password: '可选加密密码',
});

// 获取第一个账户的公钥和地址
const account: Account = wallet.accounts[0];
const publicKey = privateKeyToPublic(account.stxPrivateKey);
const mainnetAddress = privateKeyToAddress(account.stxPrivateKey, 'mainnet');

console.log('Private key:', account.stxPrivateKey);
console.log('Address:', mainnetAddress);
// 私钥: 97ff523937735dc6c9e3180f98a6aa94f526fbe072230b99e07482260f59988c01
// 地址: SP1WNA65XE3M665RJ9AC81J18XPMJ5QC5XJDHWXE
```

{% endcode %}

有关以编程方式生成密钥和账户的更多方法，请查看指南 [这里](https://app.gitbook.com/s/Zz9BLmTU9oydDpL3qiUh/stacks.js/accounts-and-addresses).
{% endstep %}

{% step %}

#### 使用 Stacks CLI 工具

使用 Stacks CLI 工具可以让你在命令行中管理钱包。

**`make_keychain`**

```bash
# 全局安装 CLI
npm install --global @stacks/cli

# 生成新账户并将详细信息存入新文件。
# '-t' 选项会使其成为测试网账户
stx make_keychain -t > cli_keychain.json

# make_keychain 输出如下内容：
{
  "mnemonic": "aaa bbb ccc ddd ...",
  "keyInfo": {
    "privateKey": "5a3f1f15245bb3fb...",
    "address": "STJRM2AMVF90ER6G3RW1QTF85E3HZH37006D5ER1",
    "btcAddress": "biwSd6KTEvJcyX2R8oyfgj5REuLzczMYC1",
    "wif": "L4HXn7PLmzoNW...",
    "index": 0
  }
}
```

**`get_address`**

```bash
# 获取私钥或多签私钥包的地址。
# 返回 BTC 和 STACKS 地址

stx get_address f5185b9ca93bdcb5753fded3b097dab8547a8b47d2be578412d0687a9a0184cb01
{
  "BTC": "1JFhWyVPpZQjbPcXFtpGtTmU22u4fhBVmq",
  "STACKS": "SP2YM3J4KQK09V670TD6ZZ1XYNYCNGCWCVVKSDFWQ"
}

stx get_address 1,f5185b9ca93bdcb5753fded3b097dab8547a8b47d2be578412d0687a9a0184cb01,ff2ff4f4e7f8a1979ffad4fc869def1657fd5d48fc9cf40c1924725ead60942c01
{
  "BTC": "363pKBhc5ipDws1k5181KFf6RSxhBZ7e3p",
  "STACKS": "SMQWZ30EXVG6XEC1K4QTDP16C1CAWSK1JSWMS0QN"
}
```

如需更多使用示例，请查看 Stacks CLI 仓库 [这里](https://github.com/stx-labs/stacks.js/tree/main/packages/cli).
{% endstep %}

{% step %}

#### 使用比特币 L2 钱包扩展/应用

有许多流行的比特币钱包支持比特币层和元协议。Leather 和 Xverse 是开始使用 Stacks 的热门选择。

{% embed url="<https://youtu.be/RvTuex1jbGY?si=KGEGESS3sr38G_Tj>" %}
{% endstep %}

{% step %}

#### 使用其他社区构建的工具

查看 [Stacks Devtools Catalog](https://app.gitbook.com/s/Zz9BLmTU9oydDpL3qiUh/stacks-devtools-catalog) 以了解其他用于创建和管理 Stacks 钱包的创意开发工具。
{% endstep %}
{% endstepper %}

***

## Clarity 合约地址如何格式化？

在 Stacks 中， **合约地址/Principal** 可唯一标识一个已部署的智能合约，其结构由账户 principal 加上合约命名空间组成，形成唯一的链上身份：

```
<deployer-address>.<contract-name>
```

#### 结构分解

1. **部署者地址**\
   一个标准的 Stacks principal（由公钥的 hash160 派生），例如：
   * `SP...` → 主网
   * `ST...` → 测试网
2. **合约名称**\
   一个在部署时选定的 ASCII 标识符（例如， `my-token`, `amm-v1`, `nft-market`).

#### 为什么要这样构造

因为合约标识符是以部署者地址作为命名空间的：

* 两个不同的账户可以部署同名合约。
* 同一个账户不能部署两个同名合约。
* 该标识符是确定性的，并且在全局范围内唯一。

***

## 处理不同格式

新接触 Stacks 的开发者在开发中指定 Stacks 的 principal（也就是地址）时，常常会被不同方式弄混。

下面简要说明如何在 3 种不同使用场景中处理 principal。

<div data-with-frame="true"><figure><img src="https://629346125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FH74xqoobupBWwBsVMJhK%2Fuploads%2F9habwlXLqQ25fu6TUVM5%2Fhandling-principal-formats.jpeg?alt=media&#x26;token=08fcc12c-ee57-48e9-9ab5-a18233773fe2" alt=""><figcaption></figcaption></figure></div>

***

### Stacks 与比特币地址的关联

Stacks 与其 L1 结算层比特币之所以紧密相连，是因为它们有许多共同点。其中之一就是两者都采用基于 P2PKH 格式的相似地址生成方案，这使得比特币和 Stacks 地址可以共享同一个公钥哈希。如果你对一个传统比特币地址进行 base58check 解码，就可以揭示其公钥哈希，然后可用它生成对应的 c32check 编码 Stacks 地址。

在程序上，你也可以使用一个名为 `b58ToC32`的 `c32check` JavaScript 库中的方法，它可以帮你抽象化转换过程。

<div data-with-frame="true"><figure><img src="https://629346125-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FH74xqoobupBWwBsVMJhK%2Fuploads%2FVqWJul0cMmHoNGXiwsg3%2Fbitcoin-stacks-address-connection.jpeg?alt=media&#x26;token=90fe1ed0-29dc-4a1f-a2d4-0a89d57acc23" alt=""><figcaption></figcaption></figure></div>

***

### 其他资源

* \[[Hiro 博客](https://www.hiro.so/blog/understanding-the-differences-between-bitcoin-address-formats-when-developing-your-app)] 理解比特币地址格式之间的差异
* \[[Hiro 博客](https://www.hiro.so/blog/how-every-stacks-address-has-a-corresponding-bitcoin-address)] 每个 Stacks 地址如何对应一个比特币地址
* \[[Hiro 博客](https://www.hiro.so/blog/an-intro-to-web3-wallets-for-web3-founders)] Web3 创始人 Web3 钱包入门
* \[[Hiro 博客](https://www.hiro.so/blog/why-web3-needs-bitcoin-centric-wallet-standards)] 为什么 Web3 需要以比特币为中心的钱包标准
* \[[Stacks YT](https://youtu.be/RvTuex1jbGY?si=KGEGESS3sr38G_Tj)] 如何设置你的第一个比特币 L2 钱包
* \[[SIP-005](https://github.com/stacksgov/sips/blob/main/sips/sip-005/sip-005-blocks-and-transactions.md)] 关于钱包/账户生成方式的技术解析和标准。
