🟢
Aptos RU WiKi
  • Aptos Developer Network
  • Основы
    • Учётные записи (аккаунты)
    • События (events)
    • Полные ноды
    • Газ и комиссия за транзакции
    • Подтверждение
    • Топология сети
    • Транзакции и состояния
    • Валидирующие ноды
  • Гайды
    • Начало работы
    • Жизнь транзакции
    • Move в сети Aptos
    • Взаимодействие с блокчейном Aptos
  • Руководства по применению
    • Ваша первая транзакция
    • Ваш первый Move модуль
    • Ваш первый Dapp
    • Ваша первая монета
    • Ваш первый NFT
    • Расширение Wallet
  • Руководства по нодам
    • Запуск локального тестнета
    • Запуск полной ноды
      • Запуск полной ноды
      • Обновление полной ноды на новый релиз
      • Идентификация для полной ноды
      • Устранение неполадок при настройке полной ноды
  • Документы по вознаграждаемому тестнету
    • Введение
    • Запуск валидатора используя GCP
    • Запуск валидатора используя AWS
    • Запуск валидатора используя Azure
    • Запуск валидатора используя Docker
    • Запуск валидатора используя исходные файлы
    • Подключение к вознаграждаемому тестнету Aptos
  • Критерии работоспособности ноды
  • Телеметрия
  • Глоссарий
Powered by GitBook
On this page
  • Токены и NFT в сети Aptos
  • Определение Aptos Token
  • Представление Токенов
  • Задачи для реализации по Токенам
  1. Руководства по применению

Ваш первый NFT

PreviousВаша первая монетаNextРасширение Wallet

Last updated 2 years ago

Примечание: работа над данным руководством еще не завершена. Кроме того, спецификация Aptos' (Non-Fungible) Token (невзаимозаменяемого Aptos токена) не формализована.

Токены и NFT в сети Aptos

- это невзаимозаменяемый токен или данные, хранящиеся в блокчейне, которые однозначно определяют право собственности на актив. NFT были впервые определены в , а затем расширены в . NFT обычно включают следующие аспекты:

  • Имя - имя актива, которое должно быть уникальным в коллекции

  • Описание - описание актива

  • URL-адрес - неопределенный указатель вне сети для получения дополнительной информации об активе, может быть медиа-объект, такой как изображение или видео, или другие метаданные

  • Выпуск (supply) - общее количество единиц этого NFT, многие NFT представлены в единичном исполнении, в то время как другие в количестве более одного называются изданиями (editions)

Кроме того, большинство NFT являются частью коллекции или набора NFT с общим атрибутом, например, темой, создателем или минимальным контрактом. Каждая коллекция имеет похожий набор атрибутов:

  • Имя - название коллекции, которое должно быть уникальным в учетной записи автора

  • Описание - описание актива

  • URL-адрес - неопределенный указатель вне сети для получения дополнительной информации об активе, может быть медиа-объект, такой как изображение или видео, или другие метаданные

Реализацию Aptos для основных NFT или Токенов можно найти здесь .

Определение Aptos Token

Токен

Aptos Token определяется как:

id

GUID:ID

Глобальный уникальный идентификатор этого токена, также необходим для идентификации автора

name

ASCII::String

Имя этого токена должно быть уникальным в рамках коллекции

collection

GUID:ID

Глобальный уникальный идентификатор коллекции, содержащей этот токен

balance

u64

Текущее хранимое количество данного токена по отношению к общему выпуску (supply), 1 <= balance <= supply

Aptos TokenData определяется как:

Field
Type
Description

id

GUID:ID

Глобальный уникальный идентификатор для этого токена, также необходим для идентификации автора

description

ASCII::String

Описывает этот токен

name

ASCII::String

Имя этого токена должна быть уникальным в рамках коллекции

supply

u64

Общее количество изданий этого Токена

uri

ASCII::String

URL-адрес для дополнительной информации/медиа

metadata

TokenType

Общая опционально определяемая пользователем структура, содержащая дополнительную информацию об этом токене он-чейн

Токены определяются с помощью хранилища атрибутов move store, что означает, что их можно сохранять в глобальном хранилище. Токены не могут быть удалены неявным образом и должны быть сожжены, чтобы гарантировать, что общий баланс равен общему выпуску (supply). Токены не могут быть скопированы. То есть общий баланс или выпуск не может быть изменен никем, кроме автора, из-за отсутствия оператора копирования. Обратите внимание, что текущие API не предоставляют возможности минта после создания. Токен может быть однозначно идентифицирован либо по его id , или TokenType, collection name, and token name.

TokenData имеет атрибут copy для упрощения разделения баланса токенов. Разделение токенов может произойти всякий раз, когда пользователь с балансом больше 1 предлагает другому лицу часть своего баланса, меньшую, чем общий баланс. Пользователям, торгующим токенами, следует помнить, что два Токена могут использовать одни и те же данные TokenData, поскольку стандарт Aptos не пытается определить, копирует ли токен атрибуты другого. Повторяя то, что было сказано ранее, токен может быть однозначно идентифицирован либо по его id , либо TokenType, имени коллекции, имени токена. Автор может изменить любое значение вTokenType, имени коллекции или имени токена, чтобы создать похожие, но не идентичные токены.

Коллекция Токенов

Aptos определяет набор коллекций, сгруппированных по их уникальным id:

struct Collections<TokenType: copy + drop + store> has key {
    collections: Table<ASCII::String, Collection>,
}

struct TokenMetadata<TokenType: store> has key {
    metadata: Table<ID, TokenType>,
}

Так как Collections имеет attribute key, он хранится непосредственно в учетной записи автора. Важно отметить, что если бы не было категории Collections , а вместо этогоCollection имела бы key attribute, учетная запись Aptos могла бы иметь только одну коллекцию, что часто бывает не так. Коллекцию можно искать в наборе Коллекций по имени, что позволяет использовать уникальное имя коллекции.

Структуры Token и TokenData фиксированы по своему содержанию. РесурсTokenMetadata позволяет создателям хранить дополнительные данные токена. Данные в таблице хранятся как уникальный Token's ID. Использование этого является опциональным и требует специализации API из-за ограничения, заключающегося в том, что функции скрипта не могут поддерживать структуры или дженерики.

Каждая коллекция имеет следующие поля:

Field
Type
Description

tokens

Table<ASCII::String, TokenMetadata<TokenType>>

Отслеживает все Токены, связанные с этой коллекцией

claimed_tokens

Table<ASCII::String, address>

Отслеживает, где хранятся токены с supply == 1

description

ASCII::String

Описывает эту коллекцию

name

ASCII::String

Имя этой коллекции должно быть уникальным в учетной записи создателя для указанного TokenType

uri

ASCII::String

URL-адрес для дополнительной информации/медиа

count

u64

Общее количество отдельных токенов, отслеживаемых этой коллекцией

maximum

Option<u64>

Опционально, максимальное количество токенов, которое можно создать в этой коллекции

Коллекция не является хранилищем для накопления Токенов, поэтому вместо Tokenона содержит TokenData:

Field
Type
Description

id

GUID:ID

Глобальный уникальный идентификатор для этого токена, также необходим для идентификации автора

data

TokenData

Дополнительные данные об этом токене, это набор выпуска токена > 1

Хранение Токенов

Чтобы приобретать и хранить токены, у пользователя должна быть Gallery TokenType:

struct Gallery has key {
    gallery: Table<ID, Token>,
}

Как и Collections, это хранится как ресурс в учетной записи Aptos.

Представление Токенов

  • Для создания нового токена требуется написать код Move

  • Функция Script для создания нового токена должна быть специализирована, поскольку Move не поддерживает типы шаблонов или структуры в качестве входных аргументов

  • Типы шаблонов в функциях скрипта добавляют дополнительные трудности при написании функций скрипта

Это руководство проведет вас через процессы:

  • создания вашей собственной коллекции Токенов,

  • Токена вашего любимого кота,

  • а также передачи этого токена кому-либо другому.

В этом руководстве основное внимание будет уделено first_nft.py и повторному использованию библиотекиfirst_transaction.py из прошлого руководства.

TODO

В этом руководстве основное внимание будет уделено first_nft.ts и повторному использованию библиотекиfirst_transaction.ts из предыдущего руководства.

Создание Коллекции

Aptos Token позволяет создателям создавать ограниченные или неограниченные коллекции. Многие NFT носят ограниченный характер, когда автор намерен создать только определенное количество навсегда, что усиливает дефицит. В то время как другие токены могут иметь неограниченную природу, например, в коллекции, служащей для утилиты, со временем могут появляться новые токены. Коллекции SimpleToken могут быть созданы с помощью соответствующей функции скрипта:

Для конечной, то есть не больше, чем максимальное количество токенов maximum , которое может быть когда-либо выпущено:

public(script) fun create_finite_collection_script(
    account: signer,
    description: vector<u8>,
    name: vector<u8>,
    uri: vector<u8>,
    maximum: u64,
)

Для безлимитной, то есть количество токенов, которые можно добавить в коллекцию, не ограничено:

public(script) fun create_unlimited_collection_script(
    account: signer,
    description: vector<u8>,
    name: vector<u8>,
    uri: vector<u8>,
)

Эти функции скрипта можно запустить через REST API. Далее показано, как это сделать:

    def create_collection(self, account: Account, name: str, description, uri: str):
        """Creates a new collection within the specified account"""

        payload = {
            "type": "script_function_payload",
            "function": f"0x1::Token::create_unlimited_collection_script",
            "type_arguments": [],
            "arguments": [
                name.encode("utf-8").hex(),
                description.encode("utf-8").hex(),
                uri.encode("utf-8").hex(),
            ]
        }
        self.submit_transaction_helper(account, payload)

TODO

  /** Creates a new collection within the specified account */
  async createCollection(account: Account, name: string, description: string, uri: string) {
    const payload: { function: string; arguments: string[]; type: string; type_arguments: any[] } = {
      type: "script_function_payload",
      function: "0x1::Token::create_unlimited_collection_script",
      type_arguments: [],
      arguments: [
        Buffer.from(name).toString("hex"),
        Buffer.from(description).toString("hex"),
        Buffer.from(uri).toString("hex"),
      ],
    };
    await this.submitTransactionHelper(account, payload);
  }
  

Создание Токена

Токены могут быть созданы после создания коллекции. Для этого в токене должно быть указано то же самое имя коллекции collection_name , которое указано в ранее созданной коллекции name. Функция сценария Move для создания SimpleToken :

public(script) fun create_token_script(
    account: signer,
    collection_name: vector<u8>,
    description: vector<u8>,
    name: vector<u8>,
    supply: u64,
    uri: vector<u8>,
)

Эти функции скрипта можно запустить через REST API. Далее показано, как это сделать:

    def create_token(
            self,
            account: Account,
            collection_name: str,
            name: str,
            description: str,
            supply: int,
            uri: str,
    ):
        payload = {
            "type": "script_function_payload",
            "function": f"0x1::Token::create_unlimited_token_script",
            "type_arguments": [],
            "arguments": [
                collection_name.encode("utf-8").hex(),
                name.encode("utf-8").hex(),
                description.encode("utf-8").hex(),
                True,
                str(supply),
                uri.encode("utf-8").hex(),
                str(0),
            ]
        }
        self.submit_transaction_helper(account, payload)

TODO

  async createToken(
    account: Account,
    collection_name: string,
    name: string,
    description: string,
    supply: number,
    uri: string,
  ) {
    const payload: { function: string; arguments: any[]; type: string; type_arguments: any[] } = {
      type: "script_function_payload",
      function: "0x1::Token::create_unlimited_token_script",
      type_arguments: [],
      arguments: [
        Buffer.from(collection_name).toString("hex"),
        Buffer.from(name).toString("hex"),
        Buffer.from(description).toString("hex"),
        true,
        supply.toString(),
        Buffer.from(uri).toString("hex"),
        "0",
      ],
    };
    await this.submitTransactionHelper(account, payload);
  }
  

Передача Токена

Получение ID Токена

Чтобы передать токен, отправитель должен сначала определить идентификатор токена, зная учетную запись создателя, имя коллекции и имя токена. Это можно получить, сделав запрос в REST сервис:

    def get_table_item(self, handle: str, key_type: str, value_type: str, key: Any) -> Any:
        response = requests.post(f"{self.url}/tables/{handle}/item", json={
            "key_type": key_type,
            "value_type": value_type,
            "key": key,
        })
        assert response.status_code == 200, response.text
        return response.json()

    def get_token_balance(self, owner: str, creator: str, collection_name: str, token_name: str) -> Any:
        token_store = self.account_resource(owner, "0x1::Token::TokenStore")["data"]["tokens"]["handle"]

        token_id = {
            "creator": creator,
            "collection": collection_name,
            "name": token_name,
        }

        return self.get_table_item(
            token_store,
            "0x1::Token::TokenId",
            "0x1::Token::Token",
            token_id,
        )["value"]

    def get_token_data(self, creator: str, collection_name: str, token_name: str) -> Any:
        token_data = self.account_resource(creator, "0x1::Token::Collections")["data"]["token_data"]["handle"]

        token_id = {
            "creator": creator,
            "collection": collection_name,
            "name": token_name,
        }

        return self.get_table_item(
            token_data,
            "0x1::Token::TokenId",
            "0x1::Token::TokenData",
            token_id,
        )

    def get_collection(self, creator: str, collection_name: str) -> Any:
        token_data = self.account_resource(creator, "0x1::Token::Collections")["data"]["collections"]["handle"]

        return self.get_table_item(
            token_data,
            "0x1::ASCII::String",
            "0x1::Token::Collection",
            collection_name,
        )

TODO

  async tableItem(handle: string, keyType: string, valueType: string, key: any): Promise<any> {
    const response = await fetch(`${this.restClient.url}/tables/${handle}/item`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        key_type: keyType,
        value_type: valueType,
        key: key,
      }),
    });

    if (response.status == 404) {
      return null;
    } else if (response.status != 200) {
      assert(response.status == 200, await response.text());
    } else {
      return await response.json();
    }
  }

  async getTokenBalance(owner: string, creator: string, collection_name: string, token_name: string): Promise<number> {
    const token_store = await this.restClient.accountResource(creator, "0x1::Token::TokenStore");

    const token_id = {
      creator: creator,
      collection: collection_name,
      name: token_name,
    };

    const token = await this.tableItem(
      token_store["data"]["tokens"]["handle"],
      "0x1::Token::TokenId",
      "0x1::Token::Token",
      token_id,
    );
    return token["value"];
  }

  async getTokenData(creator: string, collection_name: string, token_name: string): Promise<any> {
    const collections = await this.restClient.accountResource(creator, "0x1::Token::Collections");

    const token_id = {
      creator: creator,
      collection: collection_name,
      name: token_name,
    };

    return await this.tableItem(
      collections["data"]["token_data"]["handle"],
      "0x1::Token::TokenId",
      "0x1::Token::TokenData",
      token_id,
    );
  }
  

Предложение Токена

Следующая функция сценария Move поддерживает передачу Token в другую учетную запись, фактически регистрируя, что другая учетная запись может претендовать на этот токен:

public(script) fun offer_script(
    sender: signer,
    receiver: address,
    creator: address,
    token_creation_num: u64,
    amount: u64,
)
    def offer_token(
            self,
            account: Account,
            receiver: str,
            creator: str,
            collection_name: str,
            token_name: str,
            amount: int
    ):
        payload = {
            "type": "script_function_payload",
            "function": f"0x1::TokenTransfers::offer_script",
            "type_arguments": [],
            "arguments": [
                receiver,
                creator,
                collection_name.encode("utf-8").hex(),
                token_name.encode("utf-8").hex(),
                str(amount),
            ]
        }
        self.submit_transaction_helper(account, payload)

TODO

  async offerToken(
    account: Account,
    receiver: string,
    creator: string,
    collection_name: string,
    token_name: string,
    amount: number,
  ) {
    const payload: { function: string; arguments: string[]; type: string; type_arguments: any[] } = {
      type: "script_function_payload",
      function: "0x1::TokenTransfers::offer_script",
      type_arguments: [],
      arguments: [
        receiver,
        creator,
        Buffer.from(collection_name).toString("hex"),
        Buffer.from(token_name).toString("hex"),
        amount.toString(),
      ],
    };
    await this.submitTransactionHelper(account, payload);
  }
  

Запрос Токена

Следующая функция скрипта Move в SimpleToken поддерживает получение токена, фактически запрашивая токен:

public(script) fun claim_script(
    sender: signer,
    receiver: address,
    creator: address,
    token_creation_num: u64,
    amount: u64,
)
    def claim_token(
            self,
            account: Account,
            sender: str,
            creator: str,
            collection_name: str,
            token_name: str,
    ):
        payload = {
            "type": "script_function_payload",
            "function": f"0x1::TokenTransfers::claim_script",
            "type_arguments": [],
            "arguments": [
                sender,
                creator,
                collection_name.encode("utf-8").hex(),
                token_name.encode("utf-8").hex(),
            ]
        }
        self.submit_transaction_helper(account, payload)

TODO

  async claimToken(account: Account, sender: string, creator: string, collection_name: string, token_name: string) {
    const payload: { function: string; arguments: string[]; type: string; type_arguments: any[] } = {
      type: "script_function_payload",
      function: "0x1::TokenTransfers::claim_script",
      type_arguments: [],
      arguments: [
        sender,
        creator,
        Buffer.from(collection_name).toString("hex"),
        Buffer.from(token_name).toString("hex"),
      ],
    };
    await this.submitTransactionHelper(account, payload);
  }
  

Задачи для реализации по Токенам

  • Добавить возможность дополнительного минта

  • Убедиться, что был произведен хотя бы один токен во время минта

  • Добавить события — требуется фидбэк о том, какие события

  • Предоставить изменяемые API для токенов

  • Написать дымовое тестирование для дженериков и простых токенов напрямую

  • Обеспечить безопасное сжигание

Как часть нашей основной платформы, Aptos предоставляет базовый интерфейс токена без каких-либо дополнительных данных или явно такой, в котором ресурс TokenMetadata не имеет записи для этого токена. Мотивация такого решения включает следующее:

Данное руководство основано на разделе в качестве библиотеки для этого примера. Руководство содержит пример кода, который можно загрузить, см. пример ниже:

Вы найдете python project

Вы найдете typescript project

В Aptos и Move каждый токен занимает определенное место и на него имеется право собственности. Из-за этого передача токенов не является односторонней и требует двухэтапного процесса, аналогичного доске объявлений. Отправитель должен сначала зарегистрировать, что токен доступен для запроса получателем, а затем получатель должен потребовать этот токен. Это реализовано в экспериментальном move модуле . SimpleToken предоставляет несколько функций-оболочек для поддержки трансфера токена в другую учетную запись, подтверждения трансфера или его остановки.

NFT
EIP-721
EIP-1155
Token.move
Token
Ваша первая транзакция
здесь
здесь
TokenTransfer