Руководство для разработчиков APTOS | Ваше первое NFT | Typescript
EgorMajj
Posted on August 23, 2022
*ВНИМАНИЕ
*> Данное руководство является незавершенным. Спецификация Aptos (Non-Fungible) Token не была формализована.
Ваше первое NFT
NFT - это невзаимозаменяемый токен или данные, хранящиеся в блокчейне, которые однозначно определяют право собственности на актив. Впервые NFT были определены в EIP-721, а затем расширены в EIP-1155. Как правило, NFT состоят из следующих аспектов:
- Имя, название актива, которое должно быть уникальным в пределах коллекции
- Описание, характеристика актива
- URL, не описанный указатель вне сети на дополнительную информацию об активе, это может быть медиа, например, изображение или видео, или дополнительные метаданные.
- Предложение, общее количество единиц данного NFT, многие NFT имеют только одну единицу предложения, а те, которые имеют более одной, называются коллекциями.
Кроме того, большинство NFT являются частью коллекции или набора NFT с общим атрибутом, например, темой, создателем или минимальным количеством контрактов. Каждая коллекция имеет аналогичный набор атрибутов:
- Имя, название коллекции, которое должно быть уникальным в рамках учетной записи создателя.
- Описание, характеристика актива
- URL, не описанный указатель вне сети на дополнительную информацию об активе, это может быть медиа, например, изображение или видео, или дополнительные метаданные.
Стандарт токенов цифровых активов Aptos
Стандарт токенов Aptos разрабатывается в соответствии со следующими принципами:
- Обеспечить стандартную реализацию для улучшения совместимости между проектами экосистемы.
- Достижение максимальной ликвидности путем определения NFT, Fungible (недесятичных) и Semi-Fungible токенов в одном контракте. Эти различные типы токенов могут легко храниться, передаваться и совершаться одинаковым образом.
- Возможность настройки свойств токенов, пользователи могут определять свои собственные свойства и хранить их в сети.
- Снизить затраты на майнинг большого количества токенов NFT. Мы поддерживаем
lazy
минт на сети с помощью Semi-Fungible токенов
РЕАЛИЗАЦИЯ APTOS ДЛЯ ОСНОВНЫХ NFT
Реализацию Aptos для основных NFT или токенов можно найти в Token.move.
Определения токенов Aptos
Модель данных токена
Данные, связанные с токеном, хранятся как в учетной записи создателя, так и в учетной записи владельца.
Ресурс, хранящийся по адресу создателя:
- Collections - Содержит таблицу
collection_data
, которая сопоставляет имя коллекции сCollectionData
. В ней также хранятся все TokenData, которые создал данный создатель. - CollectionData - Хранит метаданные коллекции.
supply
- количество токенов, созданных для текущей коллекции.maxium
максимальное количество токенов в данной коллекции. - CollectionMutabilityConfig - Определение об изменении коллекции
- TokenData - Основная структура для хранения метаданных токена. Properties - это место, где пользователь может добавить свои собственные свойства, которые не определены в данных токена. Пользователь может создавать несколько токенов на основе данных TokenData, и они используют одни и те же данные TokenData.
- TokenMutabilityConfig - Контролирует, какие значения могут быть выполнены.
- TokenDataId - Идентификатор, используемый для представления и запроса TokenData в сети. Этот идентификатор в основном содержит 3 значения, включая адрес создателя, имя коллекции и имя токена.
- Royalty - Укажите знаменатель и числитель для расчета роялти. Здесь также указывается адрес учетной записи получателя для перечисления роялти.
- PropertyValue - Содержит как значение свойства, так и тип свойства.
Ресурс хранящиеся по адресу владельца:
- TokenStore - Основная структура для хранения токена, принадлежащего данному адресу. Она сопоставляет TokenId с фактическим токеном.
- Token -
amount
- количество токенов. - TokenId -
TokenDataId
указывает на метаданные этого токена.Property_version
представляет токен с измененной PropertyMap изdefault_properties
в TokenData.
Руководство по токенам
Это руководство проведет вас через весь процесс:
- Создание собственной коллекции токенов.
- Создание токена с изображением любимой кошки.
- Дарим этот токен кому-то другому.
- Минт токена с помощью lazy в сети через изменение.
Это руководство основано на вашей первой транзакции в качестве библиотеки для данного примера. Следующее руководство содержит код примера, который можно полностью загрузить ниже:
В этом руководстве мы сосредоточимся на файле first_nft.ts
и повторно используем библиотеку first_transaction.ts
из предыдущего руководства.
Вы можете найти проект typescript здесь
Создание коллекции
Токен Aptos позволяет создателям создавать коллекции. maximum
- это общее количество токенов, которое может быть создано для данной коллекции.
public(script) fun create_collection_script (
creator: &signer,
name: String,
description: String,
uri: String,
maximum: u64,
mutate_setting: vector<bool>,
)
Эти функции скрипта можно вызывать через REST API. См. ниже:
function serializeVectorBool(vecBool: boolean[]) {
const serializer = new BCS.Serializer();
serializer.serializeU32AsUleb128(vecBool.length);
vecBool.forEach((el) => {
serializer.serializeBool(el);
});
return serializer.getBytes();
}
const NUMBER_MAX: number = 9007199254740991;
const client = new AptosClient(NODE_URL);
/** Creates a new collection within the specified account */
async function createCollection(account: AptosAccount, name: string, description: string, uri: string) {
const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
"0x3::token",
"create_collection_script",
[],
[
BCS.bcsSerializeStr(name),
BCS.bcsSerializeStr(description),
BCS.bcsSerializeStr(uri),
BCS.bcsSerializeUint64(NUMBER_MAX),
serializeVectorBool([false, false, false]),
],
),
);
const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
client.getAccount(account.address()),
client.getChainId(),
]);
const rawTxn = new TxnBuilderTypes.RawTransaction(
TxnBuilderTypes.AccountAddress.fromHex(account.address()),
BigInt(sequenceNumber),
entryFunctionPayload,
1000n,
1n,
BigInt(Math.floor(Date.now() / 1000) + 10),
new TxnBuilderTypes.ChainId(chainId),
);
const bcsTxn = AptosClient.generateBCSTransaction(account, rawTxn);
const pendingTxn = await client.submitSignedBCSTransaction(bcsTxn);
await client.waitForTransaction(pendingTxn.hash);
}
Создание токена
Токены могут быть созданы после создания collection
. Для этого в токене должна быть указана та же коллекция, что и в name
ранее созданной коллекции. Функция скрипта Move имеет следующий вид:
public entry fun create_token_script(
creator: &signer,
collection: String,
name: String,
description: String,
balance: u64,
maximum: u64,
uri: String,
royalty_payee_address: address,
royalty_points_denominator: u64,
royalty_points_numerator: u64,
token_mutate_setting: vector<bool>,
property_keys: vector<String>,
property_values: vector<vector<u8>>,
property_types: vector<String>,
)
- Поле
balance
- это начальная сумма, которая будет создана для данного токена. -
maximum
определяет максимальное количество токенов, которое будет минтиться для данного созданного TokenData. - Поле
royalty_payee_address
- это адрес, на который выплачивается royalty. - Количество
royalty_points_numerator
/royalty_points_denominator
- это процент от цены продажи (royalty
), который должен быть выплачен на адрес получателя. Это может быть адрес учетной записи одного владельца или адрес общей учетной записи, принадлежащей группе создателей. - Свойство
token_mutate_setting
описывает, является ли полеTokenData
изменяемым. -
Property_keys
,Property_values
иProperty_types
- это пары ключ-значение свойства, которые можно хранить, читать и записывать в сети.
Эти функции скрипта можно вызывать через REST API. См. ниже:
async function createToken(
account: AptosAccount,
collection_name: string,
name: string,
description: string,
supply: number | bigint,
uri: string,
) {
// Serializes empty arrays
const serializer = new BCS.Serializer();
serializer.serializeU32AsUleb128(0);
const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
"0x3::token",
"create_token_script",
[],
[
BCS.bcsSerializeStr(collection_name),
BCS.bcsSerializeStr(name),
BCS.bcsSerializeStr(description),
BCS.bcsSerializeUint64(supply),
BCS.bcsSerializeUint64(NUMBER_MAX),
BCS.bcsSerializeStr(uri),
BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(account.address())),
BCS.bcsSerializeUint64(0),
BCS.bcsSerializeUint64(0),
serializeVectorBool([false, false, false, false, false]),
serializer.getBytes(),
serializer.getBytes(),
serializer.getBytes(),
],
),
);
const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
client.getAccount(account.address()),
client.getChainId(),
]);
const rawTxn = new TxnBuilderTypes.RawTransaction(
TxnBuilderTypes.AccountAddress.fromHex(account.address()),
BigInt(sequenceNumber),
entryFunctionPayload,
1000n,
1n,
BigInt(Math.floor(Date.now() / 1000) + 10),
new TxnBuilderTypes.ChainId(chainId),
);
const bcsTxn = AptosClient.generateBCSTransaction(account, rawTxn);
const pendingTxn = await client.submitSignedBCSTransaction(bcsTxn);
await client.waitForTransaction(pendingTxn.hash);
}
Раздача токенов
В Aptos и Move каждый токен занимает место и имеет право собственности. В связи с этим передача токенов не является односторонней и требует двухэтапного процесса, похожего на доску объявлений. Сначала отправитель должен зарегистрировать, что токен доступен для получателя, а затем получатель должен затребовать этот токен. Это реализовано в пробном образце модуля Move под названием TokenTransfer
.
SimpleToken
предоставляет несколько обернутых функций для поддержки перевода на другую учетную запись, утверждения этого перевода или прекращения перевода.
Получение ID токена
Чтобы передать токен, отправитель должен сначала определить id токена, зная учетную запись создателя, имя коллекции и имя токена. Это можно получить с помощью запроса REST:
async function tableItem(handle: string, keyType: string, valueType: string, key: any): Promise<any> {
const getTokenTableItemRequest = {
key_type: keyType,
value_type: valueType,
key,
};
return client.getTableItem(handle, getTokenTableItemRequest);
}
async function getTokenBalance(
owner: HexString,
creator: HexString,
collection_name: string,
token_name: string,
): Promise<number> {
const token_store = await client.getAccountResource(owner, "0x3::token::TokenStore");
const token_data_id = {
creator: creator.hex(),
collection: collection_name,
name: token_name,
};
const token_id = {
token_data_id,
property_version: "0",
};
const token = await tableItem(
(token_store.data as any)["tokens"]["handle"],
"0x3::token::TokenId",
"0x3::token::Token",
token_id,
);
return token.amount;
}
async function getTokenData(creator: HexString, collection_name: string, token_name: string): Promise<any> {
const collections = await client.getAccountResource(creator, "0x3::token::Collections");
const token_data_id = {
creator: creator.hex(),
collection: collection_name,
name: token_name,
};
const token = await tableItem(
(collections.data as any)["token_data"]["handle"],
"0x3::token::TokenDataId",
"0x3::token::TokenData",
token_data_id,
);
return token;
}
Предложение токена
Следующая функция скрипта Move в Token
поддерживает передачу токена на другую учетную запись, фактически регистрируя, что другая учетная запись может претендовать на токен:
public entry fun offer_script(
sender: signer,
receiver: address,
creator: address,
collection: String,
name: String,
property_version: u64,
amount: u64,
)
async function offerToken(
account: AptosAccount,
receiver: HexString,
creator: HexString,
collection_name: string,
token_name: string,
amount: number,
) {
const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
"0x3::token_transfers",
"offer_script",
[],
[
BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(receiver.hex())),
BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(creator.hex())),
BCS.bcsSerializeStr(collection_name),
BCS.bcsSerializeStr(token_name),
BCS.bcsSerializeUint64(0),
BCS.bcsSerializeUint64(amount),
],
),
);
const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
client.getAccount(account.address()),
client.getChainId(),
]);
const rawTxn = new TxnBuilderTypes.RawTransaction(
TxnBuilderTypes.AccountAddress.fromHex(account.address()),
BigInt(sequenceNumber),
entryFunctionPayload,
1000n,
1n,
BigInt(Math.floor(Date.now() / 1000) + 10),
new TxnBuilderTypes.ChainId(chainId),
);
const bcsTxn = AptosClient.generateBCSTransaction(account, rawTxn);
const pendingTxn = await client.submitSignedBCSTransaction(bcsTxn);
await client.waitForTransaction(pendingTxn.hash);
}
*Заявление прав на токен *
Следующая функция скрипта Move в SimpleToken
поддерживает получение токена, предоставленного предыдущей функцией, фактически заявляя права на токен:
public entry fun claim_script(
receiver: signer,
sender: address,
creator: address,
collection: String,
name: String,
property_version: u64,
)
async function claimToken(
account: AptosAccount,
sender: HexString,
creator: HexString,
collection_name: string,
token_name: string,
) {
const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
"0x3::token_transfers",
"claim_script",
[],
[
BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(sender.hex())),
BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(creator.hex())),
BCS.bcsSerializeStr(collection_name),
BCS.bcsSerializeStr(token_name),
BCS.bcsSerializeUint64(0),
],
),
);
const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
client.getAccount(account.address()),
client.getChainId(),
]);
const rawTxn = new TxnBuilderTypes.RawTransaction(
TxnBuilderTypes.AccountAddress.fromHex(account.address()),
BigInt(sequenceNumber),
entryFunctionPayload,
1000n,
1n,
BigInt(Math.floor(Date.now() / 1000) + 10),
new TxnBuilderTypes.ChainId(chainId),
);
const bcsTxn = AptosClient.generateBCSTransaction(account, rawTxn);
const pendingTxn = await client.submitSignedBCSTransaction(bcsTxn);
await client.waitForTransaction(pendingTxn.hash);
}
Минт Lazy в сети
Когда Alice становится знаменитостью в своей общине, ее кошачьи NFT пользуются большим спросом. Однако Алиса не хочет оплачивать стоимость минта 10 миллионов NFT на начальном этапе. Она хочет оплачивать расходы только тогда, когда кто-то захочет получить NFT. Она может заминтить 10 миллионов не инициализированных взаимозаменяемых кошачьих токенов в одной транзакции.
Когда Jack хочет купить NFT у Alice, она может изменить один заменимый токен.
public entry fun mutate_token_properties(
account: &signer,
token_owner: address,
creator: address,
collection_name: String,
token_name: String,
token_property_version: u64,
amount: u64,
keys: vector<String>,
values: vector<vector<u8>>,
types: vector<String>,
)
Это создаст новую property_version
и создаст новый TokenId
для предыдущего не инициализированного заменимого токена (property_version
= 0), который станет NFT. Затем Alice может передать NFT Jack. Alice нужно будет оплатить стоимость создания NFT из заменимого токена только тогда, когда кто-то захочет купить.
Posted on August 23, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.