Всем привет!
Совсем недавно Waves Labs анонсировал конкурс для разработчиков приуроченный к релизу в тестовую сеть расширения языка смарт-контрактов RIDE для децентрализованных приложений Ride4Dapps!
Мы выбрали кейс DAO, так как Ventuary планирует заниматься разработкой dApp с социальными функциями: голосованием, фандрейзингом, доверительным управлением и пр.
Мы начали работу с простого примера в Q&A-сессии и в RIDE IDE — примере с общим кошельком.
Давайте разберем данный пример, проверим гипотезы и рассмотрим некоторые странности:
Пусть у нас есть Alice — dApp Owner
Boob и Cooper — партнеры Alice, сооснователи Alice-BC DAO
Neli — владелец бизнеса, которой нужно финансирование
Bank — банк, раздающий токены
Этап 1. Инициализация балансов
Для того, что в тестовой сети waves получить токены, нужно обратиться к faucet и указать адрес на который отправить токены.
Адрес можно узнать в IDE, раскрыв данные аккаунта.
Выделяем Bank 10 WAVES. После проверяем, что они поступили через обозреватель блоков и транзакций: обозреватель
Теперь давайте раздадим токены из банка остальным участникам. (Notes: Все транзакции в сети waves не бесплатны, поэтому необходим минимальный положительный баланс у всех участников, чтобы совершать транзакции).
1 WAVES = 100000000 единиц (wavelets), так как amounts могут быть только integer
0.01 WAVES (Transaction Fee) = 1000000
Bank -> [3 WAVES] -> Alice, через TransferTransaction (Type: 4).
Проверяем, что env.SEED, от которого подписываются транзакции соответствует нашему Bank:

Если у вас нет соответствия seed фраз, просто переключитесь в него во вкладке Accounts и проверьте еще раз.
После этого создаем, анонсируем и подписываем транзакцию о передаче 3 WAVES Alice.
Узнать данные Алисы можно также через переменную env.accounts. Нумерация начинается с 0, соотвественно Алиса это env.accounts[1].
broadcast(transfer({recipient:address(env.accounts[1]), amount: 300000000, fee: 1000000}))
Результат можно также наблюдать в обозревателе, ссылка на него нам возвратится сразу после исполнения транзакции.
Убеждаемся, что баланс Alice пополнен на 3 WAVES, а на балансе банка осталось 10 — 3 — 0.01 = 0.699.
Отправляем Boob и Cooper по 3 WAVES, а Neli, Xena и Mark по 0.2 WAVES тем же способом.
(Notes: Мы допустили ошибку на один знак и отправили Neli 0.02 WAVES. Будьте внимательны!)
broadcast(transfer({recipient:address(env.accounts[4]), amount: 20000000, fee: 1000000}))
После пополнения балансов всех участников мы видим:
Этап 2. Создание dApp аккаунта
Мы договорились, что создателем и оунером децентрализованного приложения будет Alice.
В Accounts переходим устанавливаем ее, как SEED и проверяем env.SEED соответствует Alice.
Попробуем установить на аккаунт Alice самый простой скрипт (контракт) из возможных.
Смарт-контакты в Waves — это предикаты, которые запрещают или позволяют выполниться какому либо типу исходящей транзакции при определенных условиях. В данном случае это условие — ALWAYS. Код контракта — true. Вызываем deploy().
Fee за setScript транзакцию 1400000/100000000 = 0.014 WAVES. У Алисы на балансе осталось 2.986 WAVES.
Попробуем сейчас установить на аккаунт Alice более сложную логику смарт-контракта, описанную в примере
Ride4Dapps теперь включает 2 новых типа аннотаций:
- @Callable(i) — принимает в качестве параметра i, данные о том, какой аккаунт вызвал/подписал транзакцию. Именно результат этой функции определяет изменение состояния dApp аккаунта. Другие аккаунты могут создавать транзакции и исполнять функции с этой аннотацией и менять состояние dApp аккаунта.
- @Verifier(tx) — Верификатор транзакции с параметром tx транзакции. Соответствует логики предикатов из RIDE. Именно в этом выражении можно разрешить или запретить дальнейшие изменения логики смарт-контрактов на dApp аккаунте.
Будьте внимательны! Важный момент заключается в том, что по-умолчанию скрипт на аккаунте не равен true, а использует сравнение подписи и позволяет только обладателю подписи совершать транзакции.
sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPk)
Без такой проверки, кто угодно может совершать транзакции с аккаунта!
Давайте сделаем dApp аккаунт как общий кошелек для всех участников.
Чтобы проверить то, какой сейчас контракт активен на аккаунте можно в обозревателе блоков скопировать base64 код смарт-контракта и распознать его через декомпилятор (например)
Убеждаемся, что логика смарт-контракта соответствует тому, что мы ожидаем.
У Алисы на балансе осталось 2.972 WAVES.
Данный dApp ведет учет того, сколько вносят каждый из участников в общий фонд через механизм data transaction — DataEntry(currentKey, newAmount), где currentKey — это аккаунт, который вызывает функцию deposit, а newAmount — это значение пополненного баланса.
Boob и Cooper вносят свои депозиты на dApp account по 1 WAVES.
Допускаем ошибку и транзакция не проходит. Так как мы несмотря на то, что убедились в том, что делаем транзакцию от имени Bob ошиблись в индексе и указали аккаунт Bank, на котором нет смарт-контракта. Тут стоит отметить важный момент — за неудачные попытки инициировать транзакции комиссия не снимается! У Алисы на балансе осталось 2.972 WAVES. У Bob 3 WAVES.
Bob отправил 1 WAVES на dApp Account.
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"deposit",args:[]}, payment: [{amount: 100000000, asset:null }]}))
У Bob осталось 1.99 WAVES. То есть Bob заплатил 0.01 WAVES комиссии
У Алисы на балансе было 2.972 WAVES, стало 3.972. На аккаунте Alice также зарегистрирована транзакция, однако c dApp Account (Alice) никакой комиссии не было списано.
После того, как Cooper также пополнил счет у Алисы на балансе стало 4.972 WAVES.
О том, кому сколько WAVES в общем кошельке принадлежит можно узнать в обозревателе блоков во вкладке Data.
Cooper передумал оставлять сумму в 1 WAVES на общем кошельке и решил вывести половину сродств. Для этого он должен вызвать функцию withdraw.
Однако, мы снова ошиблись, так как у функции withdraw совсем другие параметры, другая сигнатура. Когда будете проектировать смарт-контракты на RIDE4DAPPS следует обратить внимание на этот момент
У Cooper на балансе стало 2.48 WAVES. Соответсвенно 3 WAVES — 1 — 0.01, а потом + 0.5 — 0.01. Соотвественно каждый вызов deposit и withdraw обходится в 0.01 WAVES. В итоге, в таблице собственников dApps записи поменялись следующим образом.
Bob также решил изъять некоторую сумму из общего кошелька, но ошибся и попытался извлечь 1.5 WAVES.
Однако в смарт-контракте была проверка на такую ситуацию.
Xena — мошенница, попробовала вывести 1 WAVES из общего счета.
У нее также ничего не вышло.
В следующей части рассмотрим более сложные моменты связанные с несовершенством Alice dApp Account.