Краткое руководство о том, как добавить дополнительные функции в контракт FA2

В выходные я решил поработать над небольшим проектом и попробовать обновленную версию контрактов, предлагаемых TQ Tezos. Эти контракты реализуют основные функции контракта FA2 в соответствии со стандартом TZIP-12. Итак, я сварил кофе, открыл VSCode и начал погружаться в код, написанный на CameLigo (теперь все это стало намного проще с новым расширением VSCode от Ligo, которое позволяет выделять синтаксис в редакторе). Пока я читал это, я подумал, что было бы интересно задокументировать мыслительный процесс и различные шаги, связанные с продлением контракта FA2 или даже любого контракта, написанного с Ligo по этому поводу. Как оказалось, это намного проще, чем вы думаете, и вы можете добиться успеха с минимальной отладкой, если сначала внимательно посмотрите на код.

В этом коротком руководстве мы расширим контракт с несколькими активами, добавив функцию «монетного двора» для создания новых токенов (стандарт FA2 не описывает уникальный способ создания токенов, это оставлено на усмотрение на усмотрение разработчиков). В общем, это то, что вы хотите делать, когда реализуете токен самостоятельно.

Так что тоже выпей кофе и присоединяйся ко мне ☕️

Скачивание репозитория

Перво-наперво, мы должны загрузить код. Перейдите к реализации контрактов FA2 от TQ Tezos, нажмите зеленую кнопку с надписью Код и скопируйте URL. Затем создайте новую папку, откройте новый терминал или окно iTerm и введите git clone + URL, который вы только что скопировали. Скачивание займет пару секунд. Затем откройте папку в VSCode, она должна выглядеть примерно так:

В этом руководстве мы собираемся взломать папку multi_asset, но, как вы можете видеть, репозиторий содержит другие виды контрактов FA2, с которыми вы можете поиграть. Теперь перейдите к multi_assetligo.

Вы найдете файл под названием «fa2_multi_token.mligo». Это первое, что мы собираемся проверить. Внизу файла вы можете найти fa2_main «настоящую» точку входа с псевдо-точками входа ниже. В начале файла вы можете увидеть структуру хранилища:

type multi_token_storage = {
    ledger : ledger;
    operators : operator_storage;
    token_total_supply : token_total_supply;
    token_metadata : token_metadata_storage;
}

Это определяет ledger (где хранятся пары адрес / баланс), operators (кому разрешено передавать токены от имени кого), token_total_supply и token_metadata (предоставляя дополнительную информацию о каждом токене).

Сбор информации о контракте

Перед тем, как начать писать код, мы должны остановиться на минуту и ​​понаблюдать за структурой контракта, с которым мы работаем. Действительно, чеканка токенов подразумевает добавление новых данных в контракт, поэтому мы должны понимать, как контракт хранит свои данные и манипулирует ими. Давайте сначала проверим хранилище!

Журнал (место, где вы храните адреса и их баланс) имеет следующую структуру:

type ledger = ((address * token_id), nat) big_map

Это большая карта, ключами которой являются пары address и token_id (что само по себе является значением nat), а значениями - nat. Теперь мы знаем, что балансы будут идентифицироваться по адресу их владельца и идентификатору токена, на который они ссылаются.

type token_total_supply = (token_id, nat) big_map

token_total_supply - это также большая карта, где ключи - это идентификатор токена (значение nat), а значения - nat. Это имеет смысл, потому что мы работаем с контрактом, который может хранить и управлять несколькими взаимозаменяемыми токенами одновременно.

Мы также можем посмотреть, как контракт хранит метаданные токена:

type token_metadata_storage = (token_id, token_metadata_michelson) big_map

Это также большая карта, где идентификатор токена используется для сопоставления метаданных. Тип token_metadata_michelson - это просто преобразование из записи, которую вы можете найти в fa2_interface.mligo:

type token_metadata = {
    token_id : token_id;
    symbol : string;
    name : string;
    decimals : nat;
    extras : (string, string) map;
}
type token_metadata_michelson = 
    token_metadata michelson_pair_right_comb

Как и следовало ожидать, метаданные токенов довольно стандартные, но мы должны принять во внимание тот факт, что они преобразуются в тип Майкельсона перед сохранением. Мы также собираемся добавить новое поле admin : address, где мы будем хранить адрес создателя токена.

Заключение договора

Пора написать код 👨‍💻

В зависимости от поведения, которое вы хотите от своего токен-минтера, у вас будет много разных вариантов чеканки токенов. В этом уроке мы позволим любому создавать и чеканить новые токены с ограниченным запасом.

Начнем с создания нового пустого файла в папке fa2 с именем «mint_tokens.mligo» и напишем объявление функции:

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

Функция принимает аргумент типа mint_tokens_params, который содержит разные значения, необходимые для создания нового токена. Мы создадим этот тип в файле fa2_interface.mligo. Важно, чтобы вы аккуратно разместили новый тип внутри файла, чтобы у вас был доступ к типам, которые были объявлены ранее. Мы поместим его непосредственно перед type fa2_entry_points =:

Нам понадобятся все вышеперечисленные детали, чтобы создать новый токен, и мы преобразуем его в пару Майкельсона, чтобы обеспечить правильную структуру (в противном случае это может вызвать неожиданное поведение).

Давайте просто добавим нашу новую точку входа Mint_tokens прямо сейчас, поскольку тип точек входа указан чуть ниже:

Вернемся к функции mint_tokens!

Чеканка токенов

Давайте сделаем паузу, чтобы подумать, чего будет достигать функция чеканки. Он принимает все параметры, описанные в mint_tokens_params, и обновляет ledger, token_total_supply и token_metadata части хранилища. Пользователь передаст идентификатор токена, контракт проверит, что он еще не существует, и создаст его со всеми предоставленными данными.

Итак, сначала давайте проверим, нет ли идентификатора токена в контракте:

Примечание. Во время разработки я обычно возвращаю ожидаемое значение в конце, чтобы интерпретатор не возвращал ошибки об этом и фокусировался на ошибках в коде. Вы также можете написать всю функцию за один раз и отладить ее шаг за шагом.

Мы используем Big_map.mem, чтобы определить, не существует ли идентификатор токена на token_total_supply большой карте. Если это так, контракт не выполняется. В противном случае он продолжает создание токена.

Теперь, когда мы проверили идентификатор токена, давайте создадим метаданные:

Во-первых, вы можете заметить, что мы создали новый тип выше, который сделает наш код более понятным. Для метаданных мы просто заполним token_metadata запись информацией, собранной из параметров. Мы добавляем наш новый идентификатор токена и различные детали, такие как символ и имя токена. Мы также добавляем адрес Tezos.sender в качестве администратора токена. Поле extras немного особенное: мы позволяем нашим пользователям передавать список кортежей, содержащих 2 строки, для заполнения поля extras по своему усмотрению, но в конечном итоге это поле является картой, поэтому мы используем List.fold и функцию make_extras для циклического просмотра списка и создадим нужную нам extras карту.

После того, как запись создана, мы преобразуем ее в правильную структуру (поскольку этого требует хранилище) перед добавлением наших новых метаданных токена в поле token_metadata хранилища.

Затем мы создаем новую поставку токенов:

Если вы помните, общее предложение - это большая карта с ключами, представляющими идентификатор токена, и значениями, указывающими его общее количество, поэтому мы просто обновляем нашу token_total_supply большую карту, указав идентификатор токена и общее количество.

На последнем этапе обновления хранилища мы добавляем новую учетную запись в бухгалтерскую книгу с указанным общим объемом:

Наконец, мы можем вернуть обновленное хранилище:

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

И это все! Вы успешно продлили контракт FA2 с функцией чеканки 🥳

Заворачивать

Расширение существующего контракта всегда кажется сложной задачей, вы должны тщательно вставлять свой код, ничего не нарушая. Однако этого не должно быть, и на самом деле это довольно просто. Вам просто нужно наблюдать за тем, как был структурирован код, и использовать существующие типы или значения для создания собственного кода. Поместите свой #include оператор в нужное место, и готово, вы создали новый контракт! Теперь возможности безграничны!

Благодарности

Это руководство было бы невозможно без работы Юджина Мишуры в репозитории TQ Tezos, где его хорошо составленные контракты позволяют другим разработчикам сэкономить много времени!

Также прочтите