Ваш первый NFT

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

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

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

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

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

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

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

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

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

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

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

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

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

Токен

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

id

GUID:ID

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

name

ASCII::String

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

collection

GUID:ID

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

balance

u64

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

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

FieldTypeDescription

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 из-за ограничения, заключающегося в том, что функции скрипта не могут поддерживать структуры или дженерики.

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

FieldTypeDescription

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:

FieldTypeDescription

id

GUID:ID

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

data

TokenData

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Вы найдете python project здесь

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

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)

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

Токены могут быть созданы после создания коллекции. Для этого в токене должно быть указано то же самое имя коллекции 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)

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

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

Получение 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,
        )

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

Следующая функция сценария 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)

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

Следующая функция скрипта 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)

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

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

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

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

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

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

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

Last updated