- Работа с ключами. Просить у пользователя ключи нельзя, но он должен как-то подписывать транзакции.
- Необходимость платить комиссию в токенах за каждую транзакцию. Как объяснить пользователям, что каждая транзакция требует комиссии в токенах платформы, и, главное, где взять токены для оплаты комиссии за первые транзакции?
Из-за этих проблем стоимость привлечения одного пользователя становится заоблачной. Например, у одного популярного 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