> For the complete documentation index, see [llms.txt](https://docs.metaid.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.metaid.io/zh/zi-chan-xie-yi/mrc-20.md).

# MRC-20

MRC-20 是基于 MetaID 的一个 Fungible Token 发行协议，用户可以通过 MRC-20 协议发行资产并制定铸造方式。与其他资产发行协议相比，MRC-20 协议最大的特点是其发行方式能够与个人链数据以及 MetaID 协议紧密结合，从而满足各种链上活动的资产发行需求。简单来说，MRC-20 是一种为满足日后各种 Web3 活动而制定的 Fungible Token 发行协议。

### 协议格式

#### 部署

**PIN 路径**：`/ft/mrc20/deploy`

后端索引器应只对符合路径的 PIN 做验证和索引。

* 该路径下的 PIN 不接受 `modify` 和 `revoke` 操作。

#### 协议格式

```json
{
  "tick": "satoshi", // 2-24 字符
  "amtPerMint": "1000", // 每次铸造获得的总代币数量 [1, 1e12]
  "mintCount": "100", // 最大允许铸造次数 [1, 1e12]
  "tokenName": "SatoshiTheLegend", // 选填，代币全名，0-48 字符
  "decimals": "8", // 选填，小数位数 0 至 12，默认值为 8
  "premineCount": "60", // 选填，部署时预先铸造的次数，默认值为 0，[0, mintCount]
  "beginHeight": "851235", // 选填，铸造事件开始的区块高度
  "endHeight": "851781", // 选填，铸造事件结束的区块高度
  "metadata": "Arbitrary Data", // 选填，可以包含额外数据如代币描述、图示等，无格式要求
  "payCheck": { // 选填，检查付款以验证铸造资格
    "payTo": "address", // 检查输出是否匹配指定地址
    "payAmount": "" // 以 satoshi 为单位，检查是否支付了指定数量的 satoshi
  },
  "pinCheck": { // 选填，检查 PIN 以验证铸造资格
    "creator": "", // 创建 PIN 的人，使用完整的 MetaID
    "path": "/", // PIN 的路径
    "count": "1", // 0~n，所需的 PIN 数量
    "lvl": "6" // PIN 的最低等级
  }
}
```

### 要点说明

* 合法的创始交易后，该 `tick` 将被分配一个唯一的 ID，该 ID 用于标识该 MRC-20 代币。该 ID 用 `pinid` 表示。
* `tick` 为全局唯一，不可重复，`tick` 的有效判定采用先见原则。
* 总供应量 = `amtPerMint` \* `mintCount`
* `metadata` 为自定义数据项，可绑定代币相关数据，可以包含简介、图片等。
* `beginHeight` 和 `endHeight` 决定了铸造时间的有效时间范围。`beginHeight` 如未指定有效区块高度，则为 Deploy 该 PIN 确认高度；`endHeight` 如未指定有效区块高度，则代表无结束时间限制。

#### `premineCount` 说明

* 部署者可设置预挖参数，数值为在 deploy 交易中直接挖取指定次数的代币。
* 预挖的 Token 在 Deploy 交易确认时直接预挖到 Deploy 使用者地址上。
* 预挖总量 = `amtPerMint` \* `premineCount`
* `premineCount` 为选项，默认值为 0，取值范围为 `0~mintCount`，范围之外的赋值视为无效。
* **例子**：一个 deploy 中设置为 `amtPerMint = 10000`, `mintCount = 100`, `premineCount = 60`，则视为 100 次的总挖取次数中预挖了 60 次，只有剩余 40 次为公开的可铸造次数。

#### `payCheck` 说明

* 如果部署者设置了 `payCheck` 项目，则每次铸造时均会检查铸造交易是否向指定地址支付了指定的 satoshi，如不符合要求则铸造无效。
* `payTo` 项目为铸造时需向指定的地址转入；`payAmount` 为铸造时需向 `payTo` 地址转移指定数量的 satoshi。

#### `pinCheck` 说明

如果部署者设置了 `pinCheck` 项目，则每次铸造时均会检查铸造交易中是否带了指定条件的 PIN，如不符合要求则铸造无效。

* `creator` 为检查 PIN 创建者是否为指定 MetaID。
* `path` 为检查 PIN 所在路径是否为指定的路径。
  * `path` 的格式请详细查看 MetaID 协议关于 `path` 的说明。
  * `path` 支持该 `path payload` 的内容匹配：
    * `/path['payload']` 匹配指定 path 位置下 payload 的全部内容。
    * `/path['key'='value']` 匹配指定 path 位置下 payload 里的 key 和 value 值。
* `count` 为检查符合条件的 PIN 的数量，默认值为 1。
* `lvl` 为检查 PIN 的等级是否符合要求。`lvl` 由 PIN 的 PoP 值决定，请详细查看 PoP 值和 `lvl` 值说明。

`checkPin` 里的 4 个参数可相互组合，形成可适应多场景的铸造要求。

#### `pinCheck` 示例

* **不限制路径和难度及创建者，任意一个 PIN 均有铸造资格**

  ```json
  "pinCheck": {
    "count": "1"
  }
  ```
* **需要一个 simplebuzz 协议的 PIN**

  ```json
  "pinCheck": {
    "path": "/protocols/simplebuzz"
  }
  ```
* **需要关注过特定 metaid 的 PIN**

  ```json
  "pinCheck": {
    "path": "/follow['metaid_of_me']"
  }
  ```
* **需要点赞过某个 PIN 的 PIN**

  ```json
  "pinCheck": {
    "path": "/protocols/paylike['liketo'='abcdefgh...']"
  }
  ```
* **/protocols 下的任意协议的 PIN（2 个）均有铸造资格**

  ```json
  "pinCheck": {
    "path": "/protocols/*",
    "count": 2
  }
  ```
* **需要拥有我的 PIN 才有铸造资格**

  ```json
  "pinCheck": {
    "creator": "abcdef0123457" // 需要 metaid 为 "abcdef0123457" 所创造的 pin
  }
  ```

### 铸造

**PIN 路径**：`/ft/mrc20/mint`

后端索引器应只对符合路径的 mint 数据做验证和索引。

* 该路径下的 PIN 不接受 `modify` 和 `revoke` 操作。

#### 协议格式

```json
{
  "id": "479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0" // tokenID，这个 tokenID 是 deploy 交易的 PINID
}
```

### 要点说明

* 协议只有一个参数，就是 `id`，代表了要铸造的 `tokenid`，`tokenid` 为 Deploy PIN 对应的 PINID。
* 铸造的 token 总量由 Deploy 的 `amtPerMint` 参数决定。如成功铸造，该次铸造的 token 总量会转移到 Mint 交易的第一个 output 的第一个 satoshi 上。
* 铸造有效性由 `indexer` 服务决定，`indexer` 根据 Deploy 中的相关条件和约束规则验证该铸造交易是否合法。判断的规则全都是链上数据，所以即便不同开发者开发的 indexer 的校验结果也应该是一致的。
* 如果 Deploy 文件有 pinCheck 要求时，Mint 交易的 input 需要指向一个或多个有效的 PIN 以完成 PIN 校验。
* 一个 PIN 在同一个 token 的 mint event 中，只能使用一次。
* 如果 Deploy PIN 有 payCheck 要求时，Mint 交易需要有一个 output 符合 payCheck 的地址和金额要求。
* Mint 不支持跨链铸造，也就是说 Mint 交易必须和其对应的 Deploy 交易处于同一链中。

#### Mint 交易构建例子

<figure><img src="/files/p4QDvwbFNlpuztL6LYQx" alt=""><figcaption><p>使用 Taproot 信封铸造，该 MRC20 的铸造条件中需要 3 个 PIN（count: 3），payCheck 条件为向地址 xxx 支付 100000 聪 BTC。</p></figcaption></figure>

### Transfer 转账

转账有两种形式：原生转账和数据转账。

#### 原生转账（直接转账）

原生转账是一种简易的、Layer 1 的转账方式，不需要依靠写入转账数据的方式，以纯粹的 UTXO 转移方式完成。

原生转账适用于不需要 MRC20 代币找零的场景，如：

1. Alice 将自己的某种 MRC20 代币 **全部** 转移给 Bob。
2. Alice 需要给 Bob 转移的某种 MRC20 代币的数量刚好是其中一个或几个 MRC20 UTXO 的数量之和。

**原生转账交易构造**

当 `input` 中带有 MRC20 UTXO，且交易中没有指明转账数据（如 OPRETURN 或 Taproot 数据），则所有 MRC20 余额将转入第一个非 OPRETURN 输出中。

<figure><img src="/files/dzKOXY0s8dFCXVsUcVbS" alt=""><figcaption><p>Native transfer</p></figcaption></figure>

### 数据转账 (MRC20 Allocation)

* 数据转账指通过在 MetaID 规范的 Taproot 信封中显式写入转账说明数据进行转账的方式。使用 JSON 格式，通过指定的规则，规定交易中传入的 `input` 中的 MRC20 余额分别分配到哪些 `output` 中。
* 数据转账适用于所有复杂的、需要有多个 `output` 分配的，或是需要进行余额找零的交易类型。
* 数据转账使用的 PIN Path 为 `/ft/mrc20/transfer`。
* 数据转账使用与 PIN 类似的数据结构，其中 `operation` 使用 `hide` 类型，不生成对应的 UTXO，不带有 `pinId`。
* 如果分配方案超过了 `input` 中包含的 MRC20 数量的总额，则该分配视为无效（invalid），退回到缺省的直接转账机制，即当前交易中所有 MRC20 余额转入第一个非 `OPRETURN` 输出中。
* `input` 中未被分配完的余额，使用缺省的直接转账机制，自动赋予第一个非 `OP_RETURN` 的输出。可以将该机制看作自动找零的行为，默认将未分配的余额部分找零到第一个输出。
* 其他未被认可的数据转账写法被视为非法，使用缺省的直接转账机制，此交易中的 MRC20 余额将转入第一个输出。

#### 示例代码

```javascript
const allocation = [
  {
    "amount": "100",
    "vout": 0,
    "id": "479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0", // 'token1'
  },
  {
    "amount": "256",
    "vout": 3,
    "id": "bcccd98a7a1250f26b57d47cfdd36a95866d4bee59c32c9d4e71a6cc1f3429a5i0", // 'token2'
  },
  {
    "amount": "300",
    "vout": 3,
    "id": "479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0", // 'token1'
  },
];

const pinData = {
  metaidFlag: 'metaid',
  operation: 'hide',
  path: '/ft/mrc20/transfer',
  contentType: 'application/json',
  payload: JSON.stringify(allocation) // 将分配数据进行 JSON 序列化放入 payload 字段
};
```

以上的转账交易中：

* 100 个 `token1` 分配给 `index:0` 输出。
* 256 个 `token2` 分配给 `index:3` 输出。
* 300 个 `token1` 分配给 `index:3` 输出。

<figure><img src="/files/cXHXG8rsJnerovmwHCDs" alt=""><figcaption><p>完全分配</p></figcaption></figure>

<figure><img src="/files/3FwfKY8KfYD48rk4KKqD" alt=""><figcaption><p>部分分配，未被指定分配的余额将自动找零归入第一个 output。</p></figcaption></figure>

<figure><img src="/files/7KPOL4bxkDWAu49ETWvC" alt=""><figcaption><p>无效分配：分配方案超过输入中的余额，此时将忽略分配方案，退回到默认的直接转账机制。</p></figcaption></figure>

## Teleport 跨链

### 跨链示例

```javascript
const allocation: MRC20Allocation = [
  {
    amount: '100',
    id: '479f8579e5dcdbef868a61541f0d55efccfee1704c8a03f07fb1d97577104d53i0', // 'token1'
    coord: 'bcccd98a7a1250f26b57d47cfdd36a95866d4bee59c32c9d4e71a6cc1f3429a5i2', // 跨链坐标，使用 pinId 结构定位 utxo
    type: 'teleport',
  },
]

const pinData = {
  metaidFlag: 'metaid',
  operation: 'hide',
  path: '/ft/mrc20/transfer',
  contentType: 'application/json',
  payload: JSON.stringify(allocation) // 将分配数据进行 JSON 序列化放入 payload 字段
}
```

### 功能说明

Teleport 实现了 MRC-20 的跨链功能，可以视为 transfer 的扩展版本。

* **transfer**：将 MRC-20 余额分配到本交易的 output 中。
* **teleport**：将余额分配到其他链的 output 中。

#### coord（坐标）

* **格式**：使用另外链中的 `${txid}i${vout}` 格式来定位目标 utxo。
* **链识别**：coord 指向的 utxo 由 indexer 识别所在链，无需在数据中直接表述。

#### 错误处理

如果 coord 指向的 utxo 不存在（indexer 无法找到），此交易依然视为有效，而 teleport 分配的余额部分视为燃烧。

#### 与 transfer 的关系

* teleport 可以与 transfer 并列于一个交易内。
* **优先级**：分配优先级高于 transfer。

<figure><img src="/files/9j7y5thGjh2JbCLzWKJT" alt=""><figcaption><p>跨链示例</p></figcaption></figure>

### Teleport 与 Transfer 可以同层

<figure><img src="/files/DWoBQsTBz5UAEUWGBGW9" alt=""><figcaption><p>跨链和转账可以同时存在</p></figcaption></figure>
