@CREXS_bot
@CREXS_bot

Вступление

Начать хотелось бы с того, что я всегда чувствовал, что где-то внутри меня в страшных муках погибает писатель. Еще в школе гуманитарные науки давались лучше и вызывали больший интерес чем науки точные. Но вот уже на висках появляется кроткая седина, а я так же глубоко в IT, как далеко от литературы. Страшно вспомнить тот путь, который пришлось пройти, и тот код, который необходимо было через себя пропустить. Как-то всегда все в основном крутилось вокруг денег: эти деньги кто-то кому-то куда отправлял бесконечно, какие-то вечные транзакции, постоянные комиссии, списания и зачисления. Этот безумный вальс закружил так сильно, что уже сложно сказать, когда впервые в голову пришла идея реализации p2p и биржи. Возможно влияние оказал кратковременный опыт взаимодействия с протоколом MT4, возможно сказались аукционы недвижимости[ссылка удалена модератором], в разработке которых я принимал участие, а может быть и просто рынок ценных бумаг - сейчас уже вообще не разберешься как все докатилось до сегодняшних реалий. Но факт остается фактом: вместо того, чтобы писать захватывающую книгу про приключения и сокровища, приходится рассуждать о менее веселом материале про криптовалютную биржу.

Пока мы не добрались до основной темы, хочется подчеркнуть некоторые важные детали, на которые стоило бы обратить внимание:

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

  2. Не принимать за аксиому: все умозаключения в этой заметке основываются исключительно на собственной практике и личном опыте, а значит они максимально субъективны и воспринимать их следует через призму недоверия в порядке убывания собственных приоритетов.

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

  4. Код в материале будет только в разделе тестирования и будет его там совсем не много, однако он довольно наглядно отражает суть происходящего.

Давайте смотреть правде в глаза: все хотят себе крипто биржу. Многие даже толком не понимают, что это такое, но точно где-то на уровне подсознания чувствуют, что это - именно то, что им нужно. Почему? Ну это модно, молодежно и не нужно ничего делать - только загребать деньги лопатой и раскидывать по мешкам. Так что запасайтесь мешками и лопатами, присаживайтесь по-удобнее: сейчас мы с вами наконец-то разберемся раз и на всегда что это за стакан такой, который должен любить каждый настоящий мужик.

Окружение

Начнем с того, что я не один. Не пытайтесь повторить это в одиночку - у вас ничего не выйдет. Самое первое, и самое важное, из того, что мне удалось понять: нужна команда и налаженные бизнес процессы. Вы можете быть крутым разработчиком на супер skills, но этого все равно не будет достаточно. На это есть ряд причин:

  • Каждый должен заниматься своим делом.

  • Всегда есть люди, которые что-то делают лучше вас.

  • Не стоит недооценивать сложность: за изящностью и простотой может быть сокрыт тихий омут, а там 100% черти.

Ваше окружение - это самое важное. Это касается людей, оборудования и техники, операционной системы, инструментария. Должны быть налажены механизмы QA и CI. Должны быть ответственные за тех. поддержку, и те, кто отвечают за контент и продвижение. А еще способные создавать качественный медиа контент. Можно начать долго перечислять, но лучше давайте остановимся.

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

Чем больше я обо всем этом думаю, тем тверже становится уверенность в том, что единственное, что всегда присутствует в любом окружении - это непредвиденные обстоятельства. Окружение не может соответствовать ожиданиям. И один из главных навыков, который удалось приобрести за долгие годы практики - это умение ошибаться, но учиться на своих ошибках. Не ошибается тот, кто ничего не делает, как бы банально это не звучало, но ошибки будут главным фактором в нашем окружении, а наша задача будет свести их до минимума.

Окружение - это те, с кем вы можете проговорить рабочие моменты. Даже желтая уточка на столе может стать частью окружения при правильном применении. Технические моменты в статье упускаются принципиально, здесь - и дальше по тексту, потому как их великое множество и если вы ищете примеры кода - вам возможно лучше перейти к разделу тестирования, который находится чуть ниже.

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

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

Любой, даже начинающий трейдер, вам скажет, что для торговле на бирже у вас не должно быть долгов. Должны быть цели, должна быть стратегия, должна быть подушка безопасности. Про стратегию поговорим в разделе продвижения, а вот на счет подушки безопасности, следует подумать самостоятельно. Если вы собираетесь делать биржу - вы должны уметь как минимум торговать на ней. Причем желательно не ограничивать себя скудным рынком криптовалюты, а пробовать ценные бумаги и металлы. Откройте брокерский счет - это хорошая практика и в любом случае она принесет плоды, в виде, как минимум, повышения финансовой грамотности. Распределение активов - важный момент не только для игры с курсами но и вообще для вашего окружения в целом.

В окружении понадобится тестовый бот, который работает на тестовых блокчейнах. Это позволит производить QA. Монеты в этих блокчейнах можно добыть из кранов, где-то это происходит в discord, как в случае с TRX и Tether TRC20, где-то в телеге, как в случае с TON, а где-то вообще придется поковыряться в поисках. Мы так же имеем тестовый бот, который можно погонять без реальных монет в условиях, приближенных к боевым:
@CREXStest_bot

P2P

peer to peer
peer to peer

Два человека на огромной планете - какова вероятность, что они найдут друг-друга? Сейчас из за нейронных сетей уже нет твердой уверенности в том, что ты не робот, друг. В этой части, хочется немного упомянуть, что взаимодействие между разными объектами - всегда очень относительная задача. И это может быть достаточно сложным процессом, особенно если объектов этих много и качество у них разное.

Разные объекты могут иметь разные состояния, что усложняет взаимодействие, а если еще принять во внимание ошибки окружения, то процесс взаимодействия может стать и вовсе невозможным или просто приведет к непредвиденным последствиям, например где-то выгорит один разработчик.

Таким образом мы определили две важные составляющие p2p - это поиск объектов взаимодействия и оценка их состояния. Важные элементы, на которые стоит обратить внимание. Например если peer1 хочет купить TRX у peer2, но peer2 в этот момент меняется с peer3, в таком случае peer1 должен получить отказ, т.к. состояния объектов не совместимы в текущий момент времени и объекты не пригодны для взаимодействия.

Что касается поиска объектов - искать конечно нужно объекты в максимально подходящем состоянии. Состояние объектов придется не только контролировать но и регулярно обновлять для того, что поддерживать максимально актуальным. Надо отметить тот факт, что именно состояние объекта будет давать наиболее интересную информацию о том, чем объект был занят и что именно хотел сделать.

Давайте оценим свое состояние - какое оно сейчас. В порядке? Когда все дела в порядке - и состояние в покое. Так же и в p2p обмене - важно найти связи с объектами, у которых все дела сделаны и они готовы к торгам.

Если простыми словами, то обменник - это возможность менять крипту на фиат, а биржа - крипту на крипту. Например у peer1 есть немного TRX и он хотел бы поменять их на ETH в виду каких-то личных потребностей, и так сложилось, что где-то на планете в этот момент сидел peer2, у которого как раз были эти самые ETH и он в принципе, по своему состоянию, был не против поменять их на TRX. Ребята начали меняться, но тут оказалось, что peer1 надо немного больше ETH, чем есть у peer2. Совершился обмен на ту сумму, что была и разбежались, но peer1 остался в состоянии, когда у него еще не все проблемы решены - ему нужно еще немного ETH.. А больше ни у кого нет. Это немного грустно, но тут на помощь приходит стакан.

Закидываем заявку peer1 в стакан и пусть он пока там бултыхается со своими ETH. Придет его время, ведь не стоит забывать про курс, который является главным флагом для любого трейдера. Наполняем стакан разными заявками. И по стаканчику для каждой пары. Звучит красиво - прям как застолье на юбилей.

Очень важен порядок следования. Получается, что если BTC сидит слева, а USDT справа, то лучше обговорить это заранее, в противном случае, они потом перепутаются и у нас начнутся проблемы.

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

Чтобы понять, как растёт количество пар, рассмотрим следующее:

  1. Когда у вас есть n валют, количество возможных пар без учёта порядка и без повторений равно n*(n-1)/2​.

  2. Когда вы добавляете одну новую валюту, она образует пары с каждой из n уже существующих валют. Таким образом, добавляется n новых пар.

При добавлении новой валюты к n существующим, общее количество пар увеличивается с n*(n-1)/2 до (n+1)*n/2. Если, например с 4 валютами, у нас было 6 торговых пар. После добавления ещё одной валюты, общее количество торговых пар увеличивается до 10.

Если же по какой-то причине окажется, что есть возможность использовать не только BTC/USDT пару но и USDT/BTC, то количество возможных комбинаций, при добавлении пятой валюты вырастет до 20, что кратно увеличивает сложность.

Следующий важный момент, на который следует обратить внимание, заключается в том, что не смотря на кажущуюся простоту все может оказаться значительно более сложным на деле. А дело в том, что каждый раз придется работать с парой значений - это сумма и валюта с одной стороны и сумма и валюта с другой. Так как у разных валют разный курс, их значения могут сильно отличаться друг от друга, в тысячи раз, а это означает, что их нужно правильно округлять, чтобы не получились нули или не появились лишние знаки после запятой. Более того, эти значения могут быть изменены в процессе обработки и сбора состояния пользователя, а это может происходить в несколько этапов, на каждым из которых необходимо как-то взаимодействовать с данными, в том числе иногда и изменять их.

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

Весь процесс грубо можно поделить на несколько общих этапов:

  1. Сбор и подготовка состояния пользователя

  2. Формирование биржевой заявки

  3. Поиск и исполнение подходящих заявок

  4. Создание платежных документов

  5. Информирование пользователей и администраторов

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

На втором этапе мы проверяем собранные данные и формируем заявку если все хорошо и состояние пользователя проходит валидацию.

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

Четвертый шаг - это документооборот, что означает добавление нового назначения платежа к нашему обменнику - "Обмен на бирже" и создание необходимых записей в таблице платежных документов и таблице биржевых сделок.

Ну и на финальном шаге - отправляем оповещения всем участникам шоу, чтобы они были в курсе.

Тестирование

/test
/test

Без тестирования тут явно ничего не получится. Здесь будет немного примеров, как мы тестируем функциональность обмена, но это лишь малая часть того, что уже покрыто тестами и совсем крупица от того, что еще предстоит покрыть. В коде достаточно много комментариев, так что для того, чтобы не дублировать информацию не буду разбирать эти кейсы отдельно.

def test_direct_exchange_step7(self):
        this = DirectExchangeFinalHandler  # обработчик тот же, что и на шаге 6
        create_transaction_function_path = 'bot.support.billing.Billing.create_transaction'

        def __make_prototype_order(amount: int) -> DirectExchangeOrder:
            prototype_order = self.find()[0]
            prototype_order.profile = self.profile2
            prototype_order.already_exchanged_amount = Decimal('0')
            prototype_order.amount = Decimal(str(amount))
            prototype_order.price = Decimal('0.00000240')
            prototype_order.order_type = DirectExchangeOrder.Types.SELL
            prototype_order.save()
            return prototype_order

        def __copy_prototype_order(prototype_order: DirectExchangeOrder, copy_numbers: int):
            for i in range(copy_numbers):
                prototype_order.pk = None
                prototype_order.save()

        # тестируем бизнес логику обмена

        # удалим все завявки
        for a in self.find():
            a.delete()

        # и сделаем 1 новую на продажу 100 TRX по цене 0.00000240 BTC
        self.offer = self.create_order(
            profile=self.profile2,
            crypto_from=self.crypto2,
            crypto_to=self.crypto1,
            amount=self.amount,
            price=self.trx_btc_price,
            order_type='sell',
        )

        self.update.callback_query["data"] = f'{this.__name__}|{self.offer.pk}|next'
        self.dp.update = self.update

        self.dp.machine.set_selected_values(profile=self.profile, key='stock_selected_order', value=self.offer.pk)
        self.dp.machine.set_selected_values(profile=self.profile, key='stock_price', value=self.offer.price)

        # протестируем покупку
        self.dp.machine.set_selected_values(profile=self.profile, key='stock_type', value=self.StockType.BUY)
        # случай 1: недостаточно средств, цена подходит
        with patch(self.__get_balance_function(is_buy=True), return_value=Decimal('0.000240')):
            with patch(create_transaction_function_path) as mock_create_transaction_function:
                with patch('bot.handlers.base.BaseHandler.answer') as answer_mock_function:
                    self.dp.machine.set_selected_values(
                        profile=self.profile, key='stock_amount', value=self.amount * 2
                    )  # 200 TRX
                    handler = this(dispatcher=self.dp)
                    self._get_result(handler=handler)  # делаем запрос
                    # вызвана для получения шаблона
                    answer_mock_function.assert_called_with(text=get_insufficient_funds_template())
                    # вызвана для получения клавиатуры
                    answer_mock_function.assert_called_with(text=get_insufficient_funds_template())
                    # проверяем, что ордера не тронуты, т.к. нет средств
                    self.assertEqual(self.offer.status, DirectExchangeOrder.Statuses.CREATED)
                    self.assertEqual(self.offer.already_exchanged_amount, Decimal('0'))
                    self.assertEqual(len(self.find()), 1)
                    # проверяем, что не было транзакций
                    mock_create_transaction_function.assert_not_called()

        # случай 2: достаточно средств, цена НЕ подходит (т.е. ниже той, по которой есть заявка)
        with patch(self.__get_balance_function(is_buy=True), return_value=Decimal('0.000240')):
            with patch(create_transaction_function_path) as mock_create_transaction_function:
                self.dp.machine.set_selected_values(
                    profile=self.profile, key='stock_amount', value=self.amount / 2
                )  # 50 TRX
                self.dp.machine.set_selected_values(
                    profile=self.profile, key='stock_price', value=self.offer.price - Decimal('0.00000001')
                )
                handler = this(dispatcher=self.dp)
                response_text = self._get_result_template(handler=handler)  # делаем запрос
                # проверим, что создался новый ордер, но старый не был тронут
                all_orders = self.find()
                self.assertEqual(len(all_orders), 2)
                self.assertEqual(self.offer.status, DirectExchangeOrder.Statuses.CREATED)
                self.assertEqual(self.offer.already_exchanged_amount, Decimal('0'))
                mock_create_transaction_function.assert_not_called()
                self.assertEqual('✅ Заявка на обмен #E3 успешно создана.', response_text)
                # почистим за собой
                all_orders[0].delete()

        # случай 3: достаточно средств, цена подходит, но заявка исполнена не полностью, т.к. не хватает предложений
        # добавим денег x2 больше чем в прошлом тесте, т.к. тут сумма увеличивается вдвое
        with patch(self.__get_balance_function(is_buy=True), return_value=Decimal('0.000500')):
            with patch(create_transaction_function_path) as mock_create_transaction_function:
                self.dp.machine.set_selected_values(
                    profile=self.profile, key='stock_amount', value=self.amount * 2
                )  # 200 TRX
                self.dp.machine.set_selected_values(profile=self.profile, key='stock_price', value=self.offer.price)
                handler = this(dispatcher=self.dp)
                response_text = self._get_result_template(handler=handler)  # делаем запрос
                self.offer.refresh_from_db()
                all_orders = self.find()
                # заявка, что была в стакане должна быть полностью исполнена
                self.assertEqual(len(all_orders), 1)
                self.assertEqual(self.offer.status, DirectExchangeOrder.Statuses.PROCESSED)
                # а остаться должна только вновь созданная заявка, исполненная частично
                new_order = all_orders[0]
                self.assertEqual(new_order.status, DirectExchangeOrder.Statuses.CREATED)
                # причем количество уже исполненной суммы должно быть одинаковым в обеих заявках
                self.assertEqual(new_order.already_exchanged_amount, self.offer.already_exchanged_amount)

        # случай 4: достаточно средств, но для исполнения по этой цене нужно несколько заявок
        # их всех хватает и даже еще остается
        # для этого формируем прототип заявки на продажу 20 TRX по цене 0.00000240 BTC
        prototype_order = __make_prototype_order(amount=20)
        # и копируем ее 9 раз
        __copy_prototype_order(prototype_order=prototype_order, copy_numbers=9)
        # проверяем, что у нас создалось ровно 9 заявок по 20 TRX + сам прототип
        self.assertEqual(len(self.find(limit=None)), 10)
        with patch(self.__get_balance_function(is_buy=True), return_value=Decimal('0.000500')):
            with patch(create_transaction_function_path) as mock_create_transaction_function:
                self.dp.machine.set_selected_values(
                    profile=self.profile, key='stock_amount', value=self.amount * Decimal('1.66')
                )  # 166 TRX
                self.dp.machine.set_selected_values(profile=self.profile, key='stock_price', value=self.offer.price)
                handler = this(dispatcher=self.dp)
                response_text = self._get_result_template(handler=handler)  # делаем запрос
                # в итоге 8 заявок по 20 TRX должны покрыть 160 TRX и уйти со стакана
                all_orders = self.find(limit=None)
                self.assertEqual(len(all_orders), 2)
                # а в ставкане должно остаться две заявки,
                # причем одна из них должна бытьчастично исполненая (6 TRX)
                self.assertEqual(all_orders[0].already_exchanged_amount, Decimal('6'))
                self.assertEqual(all_orders[1].already_exchanged_amount, Decimal('0'))

        # случай 5: достаточно средств, для исполнения по этой цене нужно несколько заявок
        # но их всех не хватает, в результате должна остаться одна заявка частично-исполненная
        all_orders = self.find(limit=None)
        all_orders[1].delete()
        prototype_order = __make_prototype_order(amount=20)
        __copy_prototype_order(prototype_order=prototype_order, copy_numbers=5)  # всего 100 TRX
        # проверяем, что у нас создалось ровно 5 заявок по 20 TRX + сам прототип
        self.assertEqual(len(self.find(limit=None)), 6)
        with patch(self.__get_balance_function(is_buy=True), return_value=Decimal('0.000500')):
            with patch(create_transaction_function_path) as mock_create_transaction_function:
                self.dp.machine.set_selected_values(
                    profile=self.profile, key='stock_amount', value=self.amount * Decimal('1.10')
                )  # 110 TRX
                self.dp.machine.set_selected_values(profile=self.profile, key='stock_price', value=self.offer.price)
                handler = this(dispatcher=self.dp)
                response_text = self._get_result_template(handler=handler)  # делаем запрос
                # в итоге 5 заявок по 20 TRX должны покрыть 100 TRX и уйти со стакана
                all_orders = self.find(limit=None)
                self.assertEqual(len(all_orders), 1)
                # ну а последняя должна быть частично-исполненая (10 TRX)
                self.assertEqual(all_orders[0].already_exchanged_amount, Decimal('10'))

Как мы видим, тут рассмотрено 5 разных случаев, которые могут произойти во время обмена, но конечно это не все возможные варианты, но основные общие механики работы они покрывают. Далее еще пара тестов, которые могут дать примерное представление о том, что происходит:

    def test_direct_exchange(self):
        handler = DirectExchangeHandler(dispatcher=self.dp)
        result = handler.handle()
        self.assertIn(get_direct_exchange_main_template(), result['text'])
        expected = get_direct_exchange_main_keyboard()
        kb = result["reply_markup"].inline_keyboard
        self.assertEqual(expected[0][0][1], kb[0][0].callback_data)
        self.assertEqual(expected[1][0][1], kb[1][0].callback_data)
        self.assertEqual(expected[2][0][1], kb[2][0].callback_data)
        # проверим кнопку истории открытых ордеров
        self.create_order(
            profile=self.profile,
            crypto_from=self.crypto1,
            crypto_to=self.crypto2,
            amount=Decimal('1'),
            price=Decimal('1'),
            order_type='buy',
        )
        result = handler.handle()
        opened_orders_btn = result["reply_markup"].inline_keyboard[1][0]
        self.assertEqual('DirectExchangeHistoryHandler|created', opened_orders_btn.callback_data)
        self.assertEqual('???? Открытые заявки · 1', opened_orders_btn.text)

    def test_start(self):
        handler = DirectExchangeStartHandler(dispatcher=self.dp)
        result = handler.handle()
        self.assertIn(get_direct_exchange_start_template(), result['text'])
        expected = get_direct_exchange_start_keyboard(context={}, crypto_format=self.crypto_format)
        kb = result["reply_markup"].inline_keyboard
        self.assertEqual(expected[0][0][1], kb[0][0].callback_data)
        CryptoCurrencyFactory.enable_direct_trade(self.crypto1)
        CryptoCurrencyFactory.enable_direct_trade(self.crypto2)
        expected = get_direct_exchange_start_keyboard(
            context={f'{self.trx}/{self.btc}': {'price': Decimal('0.001')}}, crypto_format=self.crypto_format
        )
        result = handler.handle()
        kb = result["reply_markup"].inline_keyboard
        self.assertEqual(expected[0][0][1], kb[0][0].callback_data)
        self.assertEqual(expected[1][0][1], kb[1][0].callback_data)

Тут комментарии в коде написать поленился, по этому давайте разберем немного более подробно.

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

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

  1. Сбор данных от пользователя: Этот процесс часто связан с паттерном "Стратегия" (Strategy), который позволяет выбирать алгоритм обработки данных на основе ввода пользователя. Также может быть полезен паттерн "Команда" (Command), который инкапсулирует запрос в виде объекта, позволяя параметризовать клиентов с различными запросами.

  2. Хранение данных пользователя: Здесь может быть использован паттерн "Снимок" (Memento) для сохранения и восстановления предыдущих состояний объекта без нарушения инкапсуляции. Это особенно полезно, если вам нужно отслеживать изменения данных пользователя и предоставлять возможность возврата к предыдущим состояниям.

  3. Управление состоянием пользователя: Паттерн "Состояние" (State) позволяет объекту изменять своё поведение в зависимости от своего состояния. Это идеально подходит для ситуаций, когда действия пользователя зависят от контекста, в котором он находится.

  4. Управление доступом к функционалу: "Строитель" (Builder) или "Фабрика" (Factory) могут быть использованы для создания сложных объектов с различным набором характеристик, в зависимости от контекста или ввода пользователя.

  5. Интерфейс пользователя: Может быть использован паттерн "Посредник" (Mediator) для управления сложными взаимодействиями между объектами, упрощая тем самым связи между различными компонентами системы.

Могу предложить такой набор сущностей для реализации:

  • Контекст пользователя (SelectedValues): для хранения текущего состояния пользователя, включая его данные и позицию в программе.

  • Обработчик команд (EventHandler): для обработки действий пользователя.

  • Состояние (State): для представления различных состояний пользователя в приложении.

  • Фабрика интерфейса (InterfaceFactory): для создания пользовательского интерфейса в соответствии с текущим состоянием пользователя.

Тестирование - это очень весело. Приходится постоянно запускать один и тот же код и каждый раз ошибаться, чтобы в конце концов все наладить, чтобы эти хреновы костыли наконец-то зашевелились как надо. Нет, ну правда, кажется, что на всей планете нет другой такой профессии, где приходится ошибаться так часто, хотя скорее всего я и тут ошибаюсь. Юнит-тесты - это тот маленький светлый и теплый оазис, который способен согреть уставшего разработчика на холодных, бескрайних ледяных пустынях it.

Продвижение

/link
/link

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

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

валютные пары
валютные пары

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

Самый важный момент в любой бирже - сохранность средств. Если биржа скомпрометирована, то все остальное не имеет смысла. По этой причине у нас нет открытых API, веб сайтов или других, торчащих наружу сервисов. Все приватные ключи от кошельков пользователей шифруются симметричным алгоритмом шифрования AES-256 и через Secure Copy Protocol отправляются на удаленный хост, что обеспечивает высокую надежность и максимальную сохранность средств пользователей.

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

Для создания медиа контента мы используем мощный инструмент для 3д рендеринга и анимации сцен - blender. Это позволяет задать публикациям наших новостей фирменный стиль, а так же задать хорошее настроение пользователям и админам наших каналов.

Заключение

В процессе написания каждого из разделов меня постоянно преследовал синдром самозванца. Говоря про окружение, приходит осознание, что оно далеко от идеала. В разделе p2p очевидны лишь примитивные знания математики, тестирование - это вообще какой-то тихий ужас, а маркетинг - ну это точно не самая сильная сторона материала. С другой стороны, есть, что показать, а значит и есть, что рассказать и над чем подумать. Есть с чем работать и есть куда двигаться, а это в свою очередь значит, что время потеряно не в пустую.

Посмотреть как это работает можно в @CREXS_BOT или в тестовом боте @CREXStest_bot.

Материала очень много, все необходимое и основное вынесено в справку и новостной канал, а за более детальной информацией мы ждем вас в нашей группе.

Комментарии (12)


  1. mikegordan
    08.01.2024 10:07

    1) Обычно у нас в крипте когда пишут о автоматике и ботах дают результаты чтобы понять далее по статье как это было достигнуто.

    2) Скоро у нас в Бинанс появится раздел наподобие написание ботов сторонними разработчиками для управления ПАММ счетами - можете попробовать свои силы.


  1. E1ektr0
    08.01.2024 10:07
    +3

    Что за курсовую я прочитал.

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

    Так. Это три взаимно исключающих утверждения. Объясните где лежат приватные ключи пользователей. Почему вы их шифруете если у вас нет к ним доступа. И как осуществляется p2p торговля без участия сети.

    Все приватные ключи от кошельков пользователей шифруются симметричным алгоритмом шифрования AES-256 и через Secure Copy Protocol отправляются на удаленный хост, что обеспечивает высокую надежность и максимальную сохранность средств пользователей.


    1. born2fish Автор
      08.01.2024 10:07

      Приватный ключи лежат в базе. Дамп таблицы в зашифрованном виде на удаленном сервере, на случай выхода из строя дедика, например если погибнет raid.


      1. E1ektr0
        08.01.2024 10:07

        средства хранятся на адресах пользователей и списать их от туда могут только сами пользователи.

        Т.е. все же не пользователи а вы. Обычная централизованная биржа.


        1. born2fish Автор
          08.01.2024 10:07

          Обычная централизованная биржа, и обычный мультивалютный кошелек.
          Суть в том, что ваши деньги хранятся на вашем адресе, просто вы, как пользователь, не знаете свою фразу. И как следствие, можете использовать средства только используя те инструменты, которые предоставляет UI нашего кошелька.


          1. TheRikipm
            08.01.2024 10:07
            +1

            Суть в том, что ваши деньги хранятся на вашем адресе

            Если приватный ключ лежит в вашей базе в незашифрованном виде - то этот адрес пользователю уже не принадлежит.


            1. born2fish Автор
              08.01.2024 10:07

              Спасибо, наконец-то дельное уточнение!
              1. мы шифруем ключи и в базе они хранятся в закрытом виде.

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


    1. born2fish Автор
      08.01.2024 10:07

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

      Торговля происходит по обычным правилам работы каждой из сетей. Но если например комиссия в сети биткоин взлетит до 150$ за транзакцию - условия торговли на нашей площадке никак не изменятся, в то время как у многих аналогичных площадок моментально взлетает комиссия самой биржи.


  1. someguyinnetwork
    08.01.2024 10:07

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

    Что мешает холдеру крипты после получения фиата уйти в закат ?


    1. born2fish Автор
      08.01.2024 10:07
      -1

      Мешает UI кошелька


      1. E1ektr0
        08.01.2024 10:07
        +1

        Судя по всему, вы вообще не технический специалист. Зря тут пишете.


        1. born2fish Автор
          08.01.2024 10:07

          Поставил вам лайк за отсутствие аргументации