NEAR Live Обзор контрактов | Часть 3: Белый список и стейкинг-пул

17 min read
To Share and +4 nLEARNs

Введение

Всем привет. Сегодня мы рассмотрим 2 контракта. Мы включим в обзор контракты, включающие кросс-контрактные вызовы, и поговорим  и о том, как они работают на NEAR. Если вы хотите узнать больше о том, как работает компонуемость, это хорошая статья для чтения. Первый контракт, который мы собираемся рассмотреть, называется контрактом белого списка,  он используется в основной сети для внесения в белый список пулов ставок. Это важно, потому что распределение токенов осуществляется через смарт-контракты. Мы используем контракты блокировки,  принцип работы заключается в том, что блокировка — это автономный контракт, который содержит токены, которые должны быть выпущены в течение определенного периода времени. Допустим, токены выпускаются в течение 2 лет и линейно распределяются в каждом блоке. Что мы хотели сделать, так это дать возможность делать ставки на эти токены, включая токены, которые еще не выпущены. Вы должны иметь возможность делегировать все токены, которые вы заблокировали, например, на 2 года, и начать зарабатывать вознаграждение.  Делается это в смарт-контракте, а контракт блокировки, по сути, вызывает контракт пула ставок, который мы рассматривали ранее, и передает токены из одного контракта в другой. Токены из контракта блокировки списываются с аккаунта и переходят на счет пула ставок. Если пул ставок не предоставляет необходимых гарантий, таких как возможность возврата этих токенов, то это означает, что будут проблемы. Допустим, я создаю собственный пул ставок, который позволяет мне не только держать токены, но и выводить их на любой счет. Этот тип операции позволит вам получить ликвидные активы до окончания периода выпуска. 

Вот почему мы ввели белый список, в котором пользовательские реализации пулов ставок, одобренные NEAR Foundation, могут использоваться в контрактах блокировки. В то же время мы хотели дать возможность создавать новые пулы ставок, которые уже одобрены кем-либо, без одобрения со стороны NEAR Foundation. Это позволяет любому создать новый пул ставок через стэйкинг-пул. Стэйкинг-пул — это второй контракт, который мы сегодня рассмотрим. Работает это так: когда контракт блокировки хочет делегировать средства, прежде чем это можно будет сделать, им сначала нужно выбрать стэйкинг-пул. Когда вы выбираете стэйкинг-пул, блокировка запускает транзакцию, чтобы проверить, включен ли данный идентификатор учетной записи в белый список в контракте белого списка, и если он возвращает true, что означает, что учетная запись внесена в белый список, тогда контракт блокировки может быть продолжен с делегированием. Это позволяет блокировке фактически перейти к этому контракту. Контракт стэйкинг-пула имеет некоторые гарантии и API, которые ожидает локальный контракт, и он не собирается блокировать токены владельца или забирать токены из контракта блокировки. Также это важно для графиков инвестирования сотрудников NEAR. Четырехлетний график инвестирования позволяет фонду выпустить конкретную транзакцию для блокировки этого человека, чтобы вывести все из пула ставок и вернуть инвестируемую сумму обратно в фонд NEAR в случае ухода сотрудника (например увольнение). Это была предыстория блокировки и контракта белого списка.

Оригинальный репозиторий этих контрактов можно найти на NEAR Github. Вот оригинальное видео, на котором основано это руководство:

Контракт белого списка

Основная структура

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

Он использует NEAR API, который называется LookupSet она похожа на неупорядоченный набор. Это персистентная коллекция, но в ней нет итераторов, поэтому вы не можете перебирать ключи элементов набора. Вы можете только проверить, присутствует данный элемент или нет, и вы можете добавить его в набор. Делая это, он повышает эффективность хранения и доступа от нескольких чтений до нескольких чтений. Контракт содержит несколько полей. Первый — это Foundation_account_id. Это идентификатор учетной записи, которая управляет белым списком. Это означает, что эта учетная запись может внести в белый список пулы ставок на 1, а также может внести в белый список фабрики пулов ставок. Фабрика — это контракт, который может создать новый экземпляр пула ставок. Работает это следующим образом: когда вы выполняете транзакцию в отношении фабрики ставок, внесенной в белый список этого контракта, создается новая учетная запись в качестве дополнительной учетной записи стэйкинг-пула. В нашем случае в основной сети он называется poolv1.near, который является используемой нами фабрикой стейкинг-пулов. Он создает контракт, например bisontrails.poolv1.near, на котором он развертывает код стейкинг-пула из белого списка, который может производить эта фабрика. Позже мы перейдем к фабрике пулов ставок, но в то же время она также может внести этот пул ставок в белый список. Вот как это работает. Инициализация контракта принимает только аргумент Foundation_account_id. Учетная запись фонда имеет более высокие права доступа к этому контракту.

Добытчики

Есть много добытчиков.

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

Добавление метода стэйкинг-пула

Это метод, который может быть вызван как пулом, так и фондом. Здесь мы проверяем, добавляет ли этот метод стейкинга новый staking_pool_account_id в белый список. Он проверяет, действителен ли идентификатор учетной записи, затем, если он действителен, мы проверяем, является ли это пулом. Проверяем в наборе, что вызывающий этот метод присутствует в белом списке фабрик. Если это вызвано фабрикой стейкинг-пула, то все в порядке. Если нет, то это должен быть идентификатор базовой учетной записи, иначе контракт не будет выполнен, и этот метод вызовет панику. Если мы проходим проверку разрешений, мы просто добавляем этот стейкинг-пул в белый список. Прямо сейчас у нас есть только одна реализация для пула ставок, но теоретически мы можем изменить эту реализацию, например, когда мы вводим слэшинг, и пул ставок должен иметь необходимое разрешение. Он должен поддерживать некоторый минимальный баланс. Есть еще одно изменение, когда нам нужно изменить подтвержденный контракт на период инвестирования каждого, равный 4 годам. В сети может происходить множество вещей и нам нужна возможность потенциально изменять пулы ставок. Например, если изменится логика стейкинг пула. Это позволяет создать новую фабрику, которая является лучшей версией или поддерживает что-то, что не поддерживалось ранее. Это не позволяет мгновенно изменить комиссию за вознаграждение, а позволяет изменить ее только после 7-дневного периода ожидания или какой-либо другой идеи для модификации, которая будет другим фактором.

Следующий remove_staking_pool может быть вызван только фондом, поэтому пул ставок может быть уничтожен только фондом.

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

Наконец, remove_factory также может вызываться только фондом и удаляет factory_account_id. Допустим, срок действия первой стэйкинг-пула истекает, тогда фонд может удалить его из белого списка, а также удалить все предыдущие стэйкинг-пулы из белого списка. Теперь вы не сможете выбрать один из предыдущих опросов ставок из контракта блокировки, так же есть проверка, что это вызвано фондом, который является сравнением. Это очень простой контракт,  он работает с внутренними наборами,  единственный верный метод видимый извне — это is_whitelisted.

Неизменяемость смарт-контрактов

Особенность того, как обычно ведут себя смарт-контракты, как в Ethereum так и в других платформах, заключается в том, что они неизменны сами по себе. В Ethereum внутренняя часть каждого контракта является неизменной, но они используют прокси-контракт, который позволяет им обновлять конечную точку для определенных контрактов, которые очень важны для токенов. В этом случае наши основные контракты, по сути, полностью неизменны, и мы думаем о них так, как будто мы развернем их один раз и, вероятно, не можем их изменить, потому что в противном случае вам придется делать хардфорк и убеждать всех валидаторов сделать некоторый тип миграции кода. Это важно, потому что контроль над ними должен осуществляться на уровне контракта, а не на каких-то централизованных объектах. Например, хотя фонд по-прежнему сохраняет большой контроль над пулами ставок, имея возможность удалять пулы ставок здесь, у него нет возможности занести в черный список конкретную организацию в реальном мире от развертывания пула ставок. Они по-прежнему могут создать пул ставок с максимально возможной анонимностью и создать пул ставок, не спрашивая разрешения стать валидатором в основной сети. Что-то происходит из-за децентрализации, что-то из-за ограничения контроля. Хотя предполагается, что фонд должен поддерживать сеть, возможно, что в некоторых сценариях фонд может быть вынужден сделать что-то плохое для сети. Допустим, приходит правительство и пытается заставить их. Если у них меньше возможностей для этого, то безопасность в сети выше. Когда мы разрабатываем контракт, мы думаем: «Какова сумма стоимости в этом контракте?» или «Какова способность этого контракта влиять на сеть в целом?». Если это маленькое значение, то можно сохранять доверие до тех пор, пока доверяет сообщество, если это большое значение, то это не нормально. Когда мы на самом деле перейдем к контракту на блокировку и к тому, как он разработан, вы можете увидеть, как, например, наделение правами было разработано, чтобы с одной стороны позволить фонду вывести оставшиеся средства, но в то же время помешать фонду от вывода вложенных средств. Это законный способ ведения дел, за исключением того, что он прописан в коде. Белый список является очень важным контрактом, потому что до того, как средства будут заблокированы, большинство фондов как бы контролируют сеть через локальные контракты в пулах ставок через этот конкретный белый список, поэтому было важно разработать его таким образом, чтобы он сохранял децентрализацию. и безопасность сети без передачи контроля фонду. Допустим, что-то случилось, и фонд начал действовать злонамеренно. Допустим, вы смогли создать новый пул ставок через фабрику и делегировать в пул ставок, теперь фонд не может помешать вам делегировать в этот пул ставок.

Контракт на создание пула ставок

Include Bytes Macro

Контракт staking_pool_factory — это контракт, который имеет внутренний код контракта пула ставок. В Rust это можно сделать с помощью макроса include_bytes. По сути, он берет локальный файл и встраивает его в двоичный файл в виде вектора. Что происходит, так это то, что в этом бинарном файле WebAssembly у нас может быть выделен некоторый фрагмент памяти, который представляет бинарный файл этого конкретного пула ставок. Вернемся к началу.

Структура

Это структура.

Там была некоторая информация о газе, мы вернемся к этому позже.

Существует вознаграждение_fee_fraction, которое просто скопировано из контракта пула ставок, который мы рассмотрели ранее.

Внешние контракты

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

У нас их два, у первого может быть любое имя. Мы называем его ExtSelf, поскольку он представляет собой наш собственный контракт и содержит обратный вызов метода on_staking_pool_create. Вторая черта предназначена для контракта с белым списком, который мы только что видели, под названием add_staking_pool. Вот. 

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

Инициализация

Давайте сначала пройдемся по сценарию. Когда создается фабрика, мы проверяем, что она не была инициализирована, и проверяем staking_pool_whitelist_account_id, идентификатор учетной записи контракта белого списка, во время инициализации StakingPoolFactory. Нам нужно знать staking_pool_whitelist_account_id. Именно здесь развертывается контракт белого списка, чтобы внести в белый список наши недавно созданные экземпляры пула ставок. Мы это помним, а также создаем набор уже созданных аккаунтов в конце этого фрагмента.

Основной метод

Теперь, когда создана фабрика стейкинг-пула, например, она называется poolv1.near, и фонд внес фабрику в белый список в контракте белого списка, выполнив еще одну транзакцию. Теперь, когда эта фабрика пулов ставок занесена в белый список, это означает, что у нее есть разрешение на внесение в белый список новых пулов ставок, которые она создает. Итак, теперь приходит валидатор, и они хотят создать для себя пул ставок. Как это работает, они вызывают create_staking_pool, и он принимает кучу аргументов. Первый аргумент — это префикс. Допустим, это bisontrails без суффикса текущего идентификатора учетной записи, и это происходит от имен учетных записей NEAR. Учетная запись может создать только дополнительную учетную запись или очень длинную учетную запись, поэтому фабрика стейкинг-пула создает под собой дополнительную учетную запись, которая будет называться bisontrails.poolv1.near. Как мы обсуждали ранее, owner_id — это идентификатор учетной записи владельца пула ставок. Все эти три элемента, по сути, являются аргументами, которые вы должны передать пулу ставок при его первом создании. Это аргумент, который вы можете проксировать в пул ставок. Например, staking_pool_id может быть bisontrails.near. Staking_public_key может быть ключом стейкинга от запуска узла валидатора, а вознаграждение_fee_fraction может быть, например, 10%. Обратите внимание, что этот метод также является платным, это означает, что он принимает входящий прикрепленный депозит, и первое, что он спрашивает, это: «Вы вложили достаточно депозита?» Депозит, который вам нужно прикрепить, составляет 30 NEAR, и в нем много нулей, но это потому, что он в yocto NEAR. Вам нужно прикрепить 30 NEAR в основном потому, что вам нужно покрыть состояние самого контракта во вновь созданном пуле ставок. Контракт довольно большой, это 250 килобайт, и для этого вам нужно как минимум 25 NEAR, но также потребуются дополнительные деньги для покрытия гарантийного фонда цены. Это один из тех случаев, когда у вас должен быть прикрепленный депозит, потому что вы не можете привязать к этой транзакции столько газа. Также мы не можем конвертировать газ в NEAR в рамках контракта, поэтому в идеале разделение газа остается прежним, оно используется только для вычислений, некоторых операций чтения/записи и межконтрактных вызовов. Баланс используется для хранения состояния и переводов. Так что в нашем случае это создаст новую учетную запись, а создание новой учетной записи на NEAR потребует от вас оплаты хранения этой учетной записи. Хранилищем в нашем случае будет не только сама учетная запись, но и контракт этой учетной записи. В нашем случае это код контракта стейкинг-пула.

Следующее, что мы делаем, это проверяем, что в префиксе нет точки, что означает, что это не сама дополнительная учетная запись. Затем мы создаем новый staking_pool_account_id, объединяя точку идентификатора нашей учетной записи (.) с этим новым префиксом. Мы проверяем, что новый идентификатор учетной записи пула ставок действителен. По сути, если какое-либо из этих утверждений не удастся, протокол NEAR вернет токены. Если транзакция с прикрепленным депозитом завершится неудачно, прикрепленный депозит вернется к отправителю или предшественнику, потому что только предшественник может прикрепить баланс. Здесь безопасно делать кучу утверждений. Затем мы проверяем, что owner_id пула ставок действителен. По сути, это просто набор дополнительных помощников, которые также проверены в пуле ставок. Это сделано для того, чтобы убедиться, что если вы не передаете правильные аргументы или слегка неправильные аргументы, вам лучше потерпеть неудачу раньше, чем все будет выполнено, чтобы избежать сжигания газа и блокировки потраченных токенов. Наконец, с помощью вставки мы проверяем, что пул ставок не существует. По сути, вставка вернет true, если это новый уникальный элемент, и вернет false, если элемент уже существует в наборе. Вот как работает хеш-набор Rust точно так же, как работает упорядоченный набор. Поэтому, если имя пула уже существует, мы не будем добавлять этот пул ставок или пытаться создать эту учетную запись снова. Insert делает две вещи: добавляет этот элемент в хранилище, а также возвращает true, если элемент уникален и ранее не существовал, или возвращает false, если элемент уже присутствует. Если в наборе не было этого значения, возвращается true, если в этом наборе это значение было, возвращается false.

Наконец, мы используем API среднего уровня, мы не используем наши методы необработанных затрат, но в то же время мы не используем интерфейс высокого уровня. Это работает следующим образом: мы создаем новое обещание, которое создает временную структуру в нашем NEAR SDK и запоминает получателя этого обещания. Вы можете думать об этом так, как если бы контракт выдавал транзакцию для данного идентификатора учетной записи. Мы назовем идентификатор несуществующей учетной записи пула ставок. Конечно, это не транзакция, а квитанция, но это техническая деталь. Следующее — это первое действие поверх этого обещания. Мы начинаем объединять действия в это обещание. Первое действие — create_account. Это действие создаст новую учетную запись или завершится ошибкой, если учетная запись уже существует. Затем мы вносим прикрепленный баланс. Мы вносим весь залог, который нам передали, чтобы мы не хранили его на этой фабрике, и он уйдет с той же распиской на удаленный счет. Далее мы разворачиваем контракт. Как пояснялось ранее, include_bytes — это макрос, создающий статический срез, который мы преобразуем в вектор, который будет передан действию развертывания. Это развернет код в удаленной учетной записи. Вы можете развернуть код только в той учетной записи, которой управляете, но create_account дает вам разрешение действовать так, как будто вы являетесь владельцем этой учетной записи, только для этой конкретной транзакции. Вы можете использовать метод deploy_contract, вы можете делать ставки и делать некоторые другие вещи от имени этого контракта в первой транзакции, которую вы делаете. Наконец, мы инициализируем контракт пула ставок, используя API serda. Мы берем эту структуру и сериализуем ее в JSON, метод называется new. Первый аргумент — это прикрепленный депозит к этому вызову функции. Нам это не нужно, потому что оно этого не ожидает. Следующее – это количество газа, которое вы берете из своего текущего количества газа, и сразу забираете, после чего оно переходит на обещание. Допустим, наш метод назывался 100 терагаз, терагаз — это некая единица, которая примерно понятна человеку.

Когда вы входите, у вас есть 100 терагаза, и мы говорим, что собираемся пройти базу (25 терагаза), умноженную на 2. Мы немедленно передаем 50 терагаза в вызов функции этого метода, поэтому эти 50 терагаза означают, что мы только осталось менее 50 тера газа, потому что мы уже сожгли кое-что по логике раньше. Кроме того, каждое действие, которое вы включаете в это обещание, также будет стоить вам немного газа. Например, действие развертывания будет стоить вам, чтобы передать байты из одной учетной записи в другую. Наконец, мы используем then. Затем аналогично тому, как работает javascript, он прикрепляет зависимость от предыдущего обещания. Это первое обещание, и мы говорим, что после его завершения выполните второе обещание. Это работает следующим образом: вы вызываете, скажем, bisontrails.near, вызывая этот контракт (poolv1.near), чтобы создать bisontrails.poolv1.near. Сначала мы создаем обещание для bisontrails.poolv1.near, а затем прикрепляем обратный вызов к этому API, что не очень удобно с точки зрения использования позиционных аргументов для двух разных вещей. В любом случае он вызывает текущий идентификатор учетной записи. Таким образом, он вызовет poolv1.near после выполнения первого промиса. Вот структура: bisontrails.near вызывает poolv1.near, создавая обещание пула ставок. Теперь это создает обещание для bisontrails.poolv1.near, а также создает обещание для самого себя в методе on_staking_pool.

Ему нужен результат этого метода до того, как этот метод запустится, и здесь он передает три аргумента. Он передает идентификатор staking_pool_account_id, прикрепленный_депозит и предшествующий_account_id. Итак, кто нам звонил, какую учетную запись пытались создать и сколько токенов было привязано на случай, если нам нужно будет сделать возврат. Теперь, если isontrails.poolv1.near успешно выполнен, то on_staking_pool_create получит результат выполнения. Допустим, была некоторая неправильная конфигурация, что этот метод также будет вызываться, но получит обратный вызов, говорящий о том, что он не удался. Мы вернули основной промис после, тогда это означает, что мы сначала вернули on_staing_pool_create. Это важно, потому что результат метода create_staking_pool зависит от результата промиса on_staking_pool_create. Транзакция не запускается полностью параллельно, теперь она зависит от выполнения этого конкретного метода.

Обратная связь

Первое, что мы делаем, это говорим, что это может быть вызвано только текущим контрактом с использованием assert_self, что означает, что никто другой не может вызвать это обещание.

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

Мы делаем это следующим образом: сначала мы используем два конечных метода, чтобы проверить, что количество результатов равно 1. Это инвариант, потому что мы уже знаем, что никто не может вызвать его дважды, а второй — если результат будет успешным. Если метод выполнен успешно, то мы возвращаем true, если обещание не удалось, то оно будет ложным. Итак, теперь мы знаем, был ли создан пул ставок или нет. Опять же, мы также добавили 50 тера газа к обратному вызову, так что теперь мы находимся в обратном вызове, что означает, что у нас есть только 50 тера газа.

Если нам это удастся, мы зарегистрируем, что пул был успешно создан, а затем внесем его в белый список. Затем мы вызываем другой промис из обратного вызова и присоединяем газ 25 тера. Итак, теперь мы вызываем staking_pool_whitelist_account_id, контракт белого списка. Теперь мы можем внести в белый список только что созданный пул ставок, потому что мы передали этот аргумент обратному вызову. Мы возвращаем это обещание, чтобы пока не останавливать выполнение, потому что мы хотим завершить всю транзакцию только после завершения белого списка. В Rust нет возврата, потому что если указано последнее значение без точки с запятой, то это возвращаемое значение. Если создание завершится ошибкой, это может произойти только по одной причине: если вы введете неверный ключ ристретто, как мы кратко обсуждали ранее. Если есть какая-то странная кривая, в которой вы создали свой ключ для ставок, то он потерпит неудачу. Причина, по которой это не удается, заключается в том, что депозит, который вы передали в пул ставок для создания, будет возвращен нам, а не предшественнику. У нас по этому контракту 30 РЯЗ, и нам нужно вернуть их обратно отправителю, чтобы мы их не запирали. Первое, что мы делаем, это удаляем его из списка созданных пулов ставок, потому что это не увенчалось успехом. Итак, мы говорим, что создание не удалось, и мы собираемся вернуть вам прикрепленный депозит. Теперь это не реальный прикрепленный депозит, потому что callback не получает прикрепленный депозит. Он проходит через отдельную квитанцию ​​о возмещении, которая обычно приходит перед обратным вызовом, и также принимает id-предыдущего_аккаунта. В нашем случае, если мы вызовем предшествующий_аккаунт_ид, это будет наш, потому что это обратный вызов. Нам нужно знать, кому мы должны вернуть токены, и способ, которым мы это делаем, заключается в создании промиса с использованием нового предшественника_account_id, и мы возвращаем токены, которые были прикреплены ранее. Как вы можете видеть, мы не возвращаем это обещание, мы просто говорим, что все, нас не волнует, успешно это или нет, основной возврат значения false говорит о том, что пул не удалось создать. Что происходит сейчас: транзакция продолжает выполняться, но значение будет возвращено во внешний интерфейс. Внешний интерфейс — это NEAR CLI. Вы сразу узнаете, что транзакция не удалась. Возможно, вы еще не вернете свои деньги, поэтому вы все еще ждете, пока этот конкретный возврат будет выполнен в следующем блоке, но вы уже знаете, что пул не был создан, чтобы вы могли продолжить. Это пример того, как вы можете сделать параллельное обещание. Вот как работает фактор пула ставок. Вот геттер, который проверяет, сколько было создано стейкинг-пулов, которые можно вызвать в NEAR CLI.

Заключение

На этом  мы завершаем обзор текущих контрактов NEAR. | Часть 3: Фабрика белого списка и стейкинг-пула. Спасибо, что нашли время изучить NEAR. Скоро в этой серии появится больше материалов.

8
Пролистать наверх