# 管理资产 (NFT)

本指南将全面介绍如何使用 OCAP Client 管理非同质化代币 (NFT)，也称为资产的整个生命周期。您将学习如何从零开始创建新资产、更新其属性、建立用于标准化铸造的资产工厂，以及如何从该工厂获取新资产。


## 创建新资产

您可以使用 `createAsset` 方法在区块链上创建一个独立的唯一资产。每个资产都会被分配一个唯一的链上地址，该地址由其初始属性派生而来。

```javascript icon=logos:javascript
const { wallet } = getWallet(); // 用户的钱包对象

async function createNewAsset() {
  try {
    const [hash, address] = await client.createAsset({
      moniker: 'My Unique Digital Artwork',
      data: {
        typeUrl: 'json',
        value: { 
          description: 'A one-of-a-kind piece created by Artist X.',
          imageUrl: 'https://example.com/path/to/image.png',
        },
      },
      readonly: true,
      transferrable: true,
      wallet: wallet,
    });

    console.log(`Asset creation transaction sent: ${hash}`);
    console.log(`New asset address: ${address}`);
    return address;
  } catch (error) {
    console.error('Error creating asset:', error);
  }
}

createNewAsset();
```

### 参数

<x-field-group>
  <x-field data-name="moniker" data-type="string" data-required="true" data-desc="资产的名称。"></x-field>
  <x-field data-name="parent" data-type="string" data-default="''" data-required="false" data-desc="父资产的地址（如有）。"></x-field>
  <x-field data-name="data" data-type="object" data-required="true" data-desc="资产的数据负载，必须包含 typeUrl 和 value。"></x-field>
  <x-field data-name="readonly" data-type="boolean" data-default="false" data-required="false" data-desc="如果为 true，资产在创建后无法更新。"></x-field>
  <x-field data-name="transferrable" data-type="boolean" data-default="true" data-required="false" data-desc="如果为 true，资产可以转移到另一个账户。"></x-field>
  <x-field data-name="ttl" data-type="number" data-default="0" data-required="false" data-desc="资产首次消费后的存活时间（以秒为单位）。"></x-field>
  <x-field data-name="display" data-type="object" data-required="false" data-desc="包含资产显示信息的对象。"></x-field>
  <x-field data-name="endpoint" data-type="object" data-required="false" data-desc="包含资产端点详细信息的对象。"></x-field>
  <x-field data-name="tags" data-type="string[]" data-default="[]" data-required="false" data-desc="用于对资产进行分类的字符串数组。"></x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="资产初始所有者的钱包对象。"></x-field>
  <x-field data-name="delegator" data-type="string" data-default="''" data-required="false" data-desc="通过委托授权此交易的账户地址。"></x-field>
</x-field-group>

### 返回值

一个 `Promise`，它会解析为一个包含交易哈希和新资产链上地址的数组。

<x-field data-name="response" data-type="Promise<[string, string]>" data-desc="[交易哈希, 资产地址]"></x-field>

---


## 更新现有资产

如果资产创建时设置了 `readonly: false`，您可以使用 `updateAsset` 方法修改其 `moniker` 和 `data` 字段。通过资产的唯一地址来识别该资产。

```javascript icon=logos:javascript
const { wallet } = getWallet(); // 用户的钱包对象
const assetAddress = 'z362...'; // 要更新的资产地址

async function updateExistingAsset() {
  try {
    const hash = await client.updateAsset({
      address: assetAddress,
      moniker: 'My Updated Digital Artwork',
      data: {
        typeUrl: 'json',
        value: { 
          description: 'An updated description for my unique piece.',
          imageUrl: 'https://example.com/path/to/new_image.png',
        },
      },
      wallet: wallet,
    });

    console.log(`Asset update transaction sent: ${hash}`);
  } catch (error) {
    console.error('Error updating asset:', error);
  }
}

updateExistingAsset();
```

### 参数

<x-field-group>
  <x-field data-name="address" data-type="string" data-required="true" data-desc="要更新的资产的链上地址。"></x-field>
  <x-field data-name="moniker" data-type="string" data-required="true" data-desc="资产的新名称。"></x-field>
  <x-field data-name="data" data-type="object" data-required="true" data-desc="资产的更新数据负载。"></x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="当前资产所有者的钱包对象。"></x-field>
</x-field-group>

### 返回值

一个 `Promise`，它会解析为交易哈希。

<x-field data-name="response" data-type="Promise<string>" data-desc="交易哈希"></x-field>

---


## 创建资产工厂

资产工厂是用于创建多个相似资产的模板。它定义了铸造新资产的结构、规则和逻辑，比单独创建每个资产更高效。这对于发行活动门票、证书或收藏品等用例非常理想。

```javascript icon=logos:javascript
const { wallet } = getWallet(); // 工厂所有者的钱包

const factoryDefinition = {
  name: 'Conference Ticket Factory',
  description: 'Mints tickets for the 2024 Tech Conference.',
  limit: 1000, // 最多可铸造 1000 张门票
  input: {
    // 定义铸造资产所需的数据
    type: 'object',
    properties: {
      attendeeName: { type: 'string' },
      ticketType: { type: 'string', enum: ['General', 'VIP'] },
    },
  },
  output: {
    // 定义铸造出的资产的结构
    moniker: 'Ticket for {{attendeeName}}',
    description: '{{ticketType}} admission for the 2024 Tech Conference.',
    transferrable: false, // 门票不可转让
  },
  hooks: [],
};

async function createFactory() {
  try {
    const [hash, factoryAddress] = await client.createAssetFactory({
      factory: factoryDefinition,
      wallet: wallet,
    });

    console.log(`Factory creation transaction sent: ${hash}`);
    console.log(`New factory address: ${factoryAddress}`);
  } catch (error) {
    console.error('Error creating asset factory:', error);
  }
}

createFactory();
```

### 参数

<x-field-group>
  <x-field data-name="factory" data-type="object" data-required="true" data-desc="一个定义工厂属性和铸造逻辑的对象。">
    <x-field data-name="name" data-type="string" data-required="true" data-desc="工厂的名称。"></x-field>
    <x-field data-name="description" data-type="string" data-required="true" data-desc="工厂用途的描述。"></x-field>
    <x-field data-name="limit" data-type="number" data-default="0" data-required="false" data-desc="可以从此工厂铸造的最大资产数量。0 表示无限制。"></x-field>
    <x-field data-name="trustedIssuers" data-type="string[]" data-required="false" data-desc="被授权从此工厂铸造资产的账户地址列表。"></x-field>
    <x-field data-name="input" data-type="object" data-required="true" data-desc="定义铸造资产所需的输入数据。"></x-field>
    <x-field data-name="output" data-type="object" data-required="true" data-desc="定义将被铸造的资产的结构和属性。"></x-field>
    <x-field data-name="hooks" data-type="object[]" data-required="false" data-desc="在铸造过程中执行的钩子列表。"></x-field>
    <x-field data-name="data" data-type="object" data-required="false" data-desc="与工厂一起存储的额外任意数据。"></x-field>
  </x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="工厂所有者的钱包对象。"></x-field>
</x-field-group>

### 返回值

一个 `Promise`，它会解析为一个包含交易哈希和新工厂链上地址的数组。

<x-field data-name="response" data-type="Promise<[string, string]>" data-desc="[交易哈希, 工厂地址]"></x-field>

---


## 从工厂获取资产

从工厂获取资产是一个两步过程。首先，您准备铸造数据，这使您可以预览将要创建的资产。其次，您将交易提交到区块链以正式获取该资产。

这种分离很有用，因为它允许应用程序在用户签名并提交最终交易之前，向用户展示他们将要收到的东西。

### 步骤 1：准备资产数据

`preMintAsset` 方法接收工厂地址和用户提供的输入，以生成最终的资产数据。此过程在链下发生，不需要交易。

```javascript icon=logos:javascript
const factoryAddress = 'z2...'; // 先前创建的工厂地址
const { wallet: issuerWallet } = getIssuerWallet(); // 工厂所有者或受信任的发行方
const { wallet: userWallet } = getUserWallet(); // 将拥有新资产的用户的钱包

async function prepareAssetForMinting() {
  try {
    const mintingData = await client.preMintAsset({
      factory: factoryAddress,
      inputs: {
        attendeeName: 'John Doe',
        ticketType: 'VIP',
      },
      owner: userWallet.address,
      wallet: issuerWallet,
    });

    console.log('Prepared asset data for minting:', mintingData);
    return mintingData;
  } catch (error) {
    console.error('Error preparing asset:', error);
  }
}
```

### 步骤 2：发送交易

一旦铸造数据准备好，用户（未来的资产所有者）签名并发送 `acquireAsset` 交易。来自 `preMintAsset` 的 `itx` 对象将用作有效负载。

```javascript icon=logos:javascript
async function acquireNewAsset() {
  // 首先，从步骤 1 获取铸造数据
  const itx = await prepareAssetForMinting();
  if (!itx) return;

  try {
    const hash = await client.acquireAsset({
      itx: itx,
      wallet: userWallet, // 用户的钱包签署交易
    });

    console.log(`Asset acquisition transaction sent: ${hash}`);
    console.log(`New asset will be available at address: ${itx.address}`);
  } catch (error) {
    console.error('Error acquiring asset:', error);
  }
}

acquireNewAsset();
```

### acquireAsset 的参数

<x-field-group>
  <x-field data-name="itx" data-type="object" data-required="true" data-desc="从 `preMintAsset` 方法返回的内部交易对象。"></x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="正在获取资产的用户的钱包。"></x-field>
  <x-field data-name="delegator" data-type="string" data-default="''" data-required="false" data-desc="通过委托授权此交易的账户地址。"></x-field>
</x-field-group>

### 返回值

一个 `Promise`，它会解析为 `acquireAsset` 操作的交易哈希。

<x-field data-name="response" data-type="Promise<string>" data-desc="交易哈希"></x-field>

---


## 从工厂铸造资产

除了用户主导的 `acquireAsset` 流程外，授权的发行方（例如工厂所有者或受信任的发行方）可以铸造一个资产并将其直接发送到用户的账户。此过程也使用 `preMintAsset` 来准备数据，但最终的交易是 `mintAsset`，由发行方签名。

此流程适用于空投、颁发证书或任何接收用户无需发起最终交易的情况。

### 步骤 1：准备资产数据

此步骤与获取流程相同。发行方调用 `preMintAsset` 在链下生成交易有效负载（`itx`）。

```javascript icon=logos:javascript
const factoryAddress = 'z2...'; // 工厂地址
const { wallet: issuerWallet } = getIssuerWallet(); // 工厂所有者或受信任的发行方
const userAddress = 'z1...'; // 将接收资产的用户的地址

async function prepareAssetForMinting() {
  try {
    const mintingData = await client.preMintAsset({
      factory: factoryAddress,
      inputs: {
        attendeeName: 'Jane Smith',
        ticketType: 'General',
      },
      owner: userAddress,
      wallet: issuerWallet, // 此处使用发行方的钱包
    });

    console.log('Prepared asset data for minting:', mintingData);
    return mintingData;
  } catch (error) {
    console.error('Error preparing asset:', error);
  }
}
```

### 步骤 2：发送铸造交易

发行方使用准备好的 `itx` 对象调用 `mintAsset`。交易由发行方的钱包签名，新创建的资产被分配给上一步中指定的所有者。

```javascript icon=logos:javascript
async function mintNewAsset() {
  // 首先，从步骤 1 获取铸造数据
  const itx = await prepareAssetForMinting();
  if (!itx) return;

  try {
    // 发行方的钱包签署交易
    const hash = await client.mintAsset({
      itx: itx,
      wallet: issuerWallet,
    });

    console.log(`Asset minting transaction sent: ${hash}`);
    console.log(`New asset for ${userAddress} will be available at address: ${itx.address}`);
  } catch (error) {
    console.error('Error minting asset:', error);
  }
}

mintNewAsset();
```

### mintAsset 的参数

<x-field-group>
  <x-field data-name="itx" data-type="object" data-required="true" data-desc="从 `preMintAsset` 方法返回的内部交易对象。"></x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="正在铸造资产的发行方的钱包。"></x-field>
</x-field-group>

### 返回值

一个 `Promise`，它会解析为 `mintAsset` 操作的交易哈希。

<x-field data-name="response" data-type="Promise<string>" data-desc="交易哈希"></x-field>
