- Работа с ключами. Просить у пользователя ключи нельзя, но он должен как-то подписывать транзакции.
- Необходимость платить комиссию в токенах за каждую транзакцию. Как объяснить пользователям, что каждая транзакция требует комиссии в токенах платформы, и, главное, где взять токены для оплаты комиссии за первые транзакции?
Из-за этих проблем стоимость привлечения одного пользователя становится заоблачной. Например, у одного популярного dApp в экосистеме Waves она была около 80$ (!) при LTV ниже $10. Конверсию портили именно барьеры с расширением и комиссиями.
Первая проблема часто решается с помощью браузерных расширений вроде Metamask и Waves Keeper. Но, во-первых, здесь нужно потратить слишком много усилий, а, во-вторых, результат получится не дружественным для пользователей.
Поэтому в экосистеме Waves появился Signer — он не требует предоставлять ключи dApp, но и не заставляет устанавливать браузерные расширения. В своей статье @Vladimir Zhuravlev рассказал о Waves Signer и его интеграции в приложение.
А что же насчет второй проблемы? Одних создателей dApp она просто не волнует: они считают, что пользователи просто должны где-то взять токены для оплаты комиссий. Другие требуют от пользователей привязывать банковские карты во время регистрации, а это сильно снижает мотивацию.
Сделать так, чтобы dApp не требовал от пользователя нативных токенов для оплаты комиссии, можно с помощью тестовых периодов. В Waves для этого есть два способа.
Спонсирование транзакций
Если пользователю нужно заплатить комиссию в нативных токенах вашего dApp, можно использовать механизм спонсирования транзакций. Тогда пользователи будут платить комиссию в ваших токенах, но, так как майнеры получают комиссию только в WAVES, фактически WAVES будут списываться с аккаунта, выпустившего токен.
Распишем это по шагам:
- Пользователь платит комиссию за транзакцию в ваших токенах
- Вы получаете эти токены
- С вашего аккаунта списывается соответствующее количество WAVES и уходит майнерам.
Возникает вопрос: сколько токенов заплатит пользователь, и сколько их спишется с аккаунта спонсора?
Ответ: создатель dApp может сам установить это соотношение. В момент начала спонсирования он определяет, какая сумма в его токенах соответствует минимальной комиссии (0,001 WAVES или 100 000 в минимальной фракции).
Перейдем к примерам и коду.
Для включения спонсирования нужно отправить транзакцию типа Sponsorship. Это можно сделать либо в пользовательском интерфейсе в Waves.Exchange, либо с помощью waves-transactions и такого кода:
const { sponsorship } = require('@waves/waves-transactions') const seed = 'example seed phrase' const params = { assetId: '4uK8i4ThRGbehENwa6MxyLtxAjAo1Rj9fduborGExarC', minSponsoredAssetFee: 100 } const signedSponsorshipTx = sponsorship(params, seed)
Этот код сформирует (но не отправит в блокчейн) транзакцию:
{ "id": "A", "type": 14, "version": 1, "senderPublicKey": "3SU7zKraQF8tQAF8Ho75MSVCBfirgaQviFXnseEw4PYg", "minSponsoredAssetFee": 100, "assetId": "4uK8i4ThRGbehENwa6MxyLtxAjAo1Rj9fduborGExarC", "fee": 100000000, "timestamp": 1575034734209, "proofs": [ "42vz3SxqxzSzNC7AdVY34fM7QvQLyJfYFv8EJmCgooAZ9Y69YDNDptMZcupYFdN7h3C1dz2z6keKT9znbVBrikyG" ] }
Главный параметр транзакции — minSponsoredAssetFee, задающий соответствие: 100 токенов A равны 0,001 WAVES. Значит, чтобы отправить Transfer, пользователь должен заплатить комиссию в 100 токенов A.
Но есть и ограничения на спонсирование. Использовать спонсированные токены в качестве комиссии можно только для транзакций типов Transfer и Invoke. Спонсировать токен может только аккаунт, выпустивший этот токен. То есть, вы не сможете спонсировать токены, выпущенные не вами.
Безопасность
Перед включением спонсирования надо обратить внимание на несколько моментов.
- Пользователь может использовать спонсируемые токены для операций не только с этим токеном. Например, аккаунт с токенами A на балансе может отправлять токены B, а в качестве комиссии приложить токены A.
- Пользователь может заплатить больше минимальной комиссии за транзакцию. Например, если у пользователя есть 100 000 ваших токенов, а вы задали в качестве параметра minSponsoredAssetFee 100, пользователь сможет все свои 100 000 токенов отправить в качестве комиссии. Вы получите 100 000 токенов A, а майнер получит 1000 WAVES с вашего аккаунта (100 000 / 100 = 1000), если они на нем есть.
Функция спонсирования уже долго и успешно работает на Waves, но, если у вас есть идеи, как ее улучшить, присоединяйтесь к обсуждению в WEP-2 Customizable Sponsorship.
Оплата комиссии за счет dApp
ДИСКЛЕЙМЕР: данная возможность НЕ документирована и может в будущем прекратить работать.
Второй вариант спонсирования подходит только для одного типа транзакций — InvokeScript — и это — достаточно простой механизм. Преимущество по сравнению со спонсированием — в том, что не нужно использовать свой токен.
Любой пользователь может вызвать dApp, имея 0 WAVES на своем аккаунте, но указав в качестве комиссии именно WAVES. Скрипт начнет выполняться и, если в результате на аккаунт пользователя поступят WAVES, он получит их, и с этого аккаунта спишется комиссия.
Например, есть dApp, который на основе адреса вызывающего или отправляет, или не отправляет ему 1 WAVES. Пример — небезопасный и приводится, только чтобы объяснить принцип работы. Никогда не используйте такую логику в своих децентрализованных приложениях!
@Callable(invoke) func callMeBaby() = { let callerAddress = invoke.caller.bytes.toBase58String() if (takeRight(invoke.caller.bytes, 4) == base16'ABCD') then TransferSet([ ScriptTransfer(invoke.caller, 100000000, unit) ]) else throw("You didn't win") }
Если вызывать эту функцию с аккаунта, на котором 0 WAVES и указать fee в 0,005 WAVES, скрипт все равно начнет выполняться. Если пользователь имеет право на получение 1 WAVES, транзакция будет считаться валидной и попадет в блокчейн. Пользователь СНАЧАЛА получит 1 WAVES в результате вызова, ЗАТЕМ с него будет списано 0.005 WAVES комиссии. В итоге пользователь получит на свой аккаунт 0,995 WAVES.
Внимательный читатель уже понял, что скрипт небезопасен, потому что его можно вызывать бесконечное количество раз: если аккаунт не выиграл, транзакция не попадет в блокчейн, а если выиграл, то получит 0,995 WAVES. Беспроигрышная лотерея получилась.
Правильный подход — всегда проверять права пользователя вызывать скрипт, например, иметь свой белый список.
@Callable(invoke) func callMeBaby(uuid: String) = { if (isInWhiteList(invoke.caller) && invoke.fee == 500000) then { let id = extract(invoke.transactionId) let callerAddress = toBase58String(invoke.caller.bytes) ScriptResult( WriteSet([ DataEntry(toBase58String(id), callerAddress + uuid) ]), TransferSet([ ScriptTransfer(addressFromStringValue(callerAddress), invoke.fee, unit) ]) ) } else throw() }
Замечу, что, если в будущем модель исполнения контрактов на Waves изменится и транзакции с ошибками (throw()) тоже будут записываться в блокчейн и снимать комиссию, использовать этот функционал станет невозможно.
Обсуждение описанного функционала и возможных путей развития — в этом issue на гитхаб.
Лучшие практики
Рекомендую использовать оба решения только для тестовых режимов продуктов и с проверкой всех граничных условий. Иначе вы рискуете потерять свои средства.
Присоединяйтесь к Waves Community
Читайте Waves News channel
Смотрите Waves Youtube
Подписывайтесь на Waves Twitter, Waves Subreddit