keyof в поисках типаЖили были функции без типов. Среди них были такие,
как
prop, возвращающие поля из объекта.
const prop = (obj, key) => obj[key]
Когда наступило время раздора и хаоса и пришли типы, неопытные программисты часто ошибались в том, как организовать верный вывод типов таких функций, но для любого поля результат был
any. (и жили они без noImplicitAny)
const prop = (obj: {}, key: any) => obj[key]
const example = { meow: 'meow' }
prop(example, 'meow') // type: any
"Как так!", — восклицали они, — "Ведь мы видели, что по ключам всегда получаем тип!"
И были правы. В TypeScript существует поиск типов (
Lookup Types) , который и позволяет получить связанный с ключем тип.Тогда к ним пришел важный тип и дал новое магическое слово -
keyof.Оно давало особую способность проникать в публичные имена ключей типов и создавать литеральный объединенный тип (
Union).*
const example = { meow: 'meow', murr: 'murr' }
type CatSays = keyof typeof example // "meow" | "murr"
Благодоря знанию о
keyof и Lookup Types они быстро сообразили, как затипизировать их функцию prop:
const prop<T, K extends keyof T>(obj: T, key: K) => obj[key]
Теперь они смогут спокойно жить и припевать в типобезопасном мире, типизируя любые функции, которые как-то получают только нужные поля объекта!
—
* с помощью
keyof можно получить все публичные не статические ключи пренадлежащие именно типу, потому в примере мы вынужденны получать запросом типа typeof.#w_corner_ts
TSLint to ESLint
TSLint обещает скоро уже стать deprecated. И уже пора бы переносить все проекты с ним на новые рельсы. Очевидно, что единсвенный выбор сейчас - ESLint. Ну и куда же в современном проекте без Prettier'а. Пройдемся немного по шагам, которые сделал я для подобного переноса.
1. Во первых, не хотелось переформатировать проект, потому я сразу пошел искать тулзу, которая мне переконвертирует все мои правила из TSLint, и супер, что такая уже есть - tslint-to-eslint-config.
Запускаем
3. Перименуем
4. Обновим поле
6. PROFIT! Можно удалять tslint и избавляться от правил из
Некоторые мои проекты хранятся в монорепо, тч для нормальной работы
Также
P.S. Все же с
#w_corner_ts
TSLint обещает скоро уже стать deprecated. И уже пора бы переносить все проекты с ним на новые рельсы. Очевидно, что единсвенный выбор сейчас - ESLint. Ну и куда же в современном проекте без Prettier'а. Пройдемся немного по шагам, которые сделал я для подобного переноса.
1. Во первых, не хотелось переформатировать проект, потому я сразу пошел искать тулзу, которая мне переконвертирует все мои правила из TSLint, и супер, что такая уже есть - tslint-to-eslint-config.
Запускаем
npx tslint-to-eslint-config
2. Тулза оказалась достаточно не плохой. Но все нужные пакеты для использования ESLint она сама не поставила :(. Поставим сами, за одно добавим Prettier. (хотя и создатель сказал не использовать, но я продлжаю выбирать yarn в качестве своего менеджера зависимостей, как минимум тк я подсел на workspaces)yarn add eslint eslint-config-prettier eslint-plugin-prettier prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin @typescript-eslint/eslint-plugin-tslint -D
3. Перименуем
eslintrc.js в .eslintrc.js, либо придётся добавить флаг в скрипте -c eslintrc.js.4. Обновим поле
scripts в package.json."lint": "eslint --ignore-path .gitignore --ext .js,.jsx,.ts,.tsx"
5. Добавим Prettier. Сперва в массив plugins - plugins: [..., 'prettier'], затем в rules - rules: { 'prettier/prettier': 'error', ... }
6. Почему-то tslint-to-eslint-config генерирует несколько не сущесвующих правил в плагине @typescript-eslint/eslint-plugin, тч после первого запуска lint'a вам придется их подтереть или поискать альтернативы.6. PROFIT! Можно удалять tslint и избавляться от правил из
@typescript-eslint/tslint плагина.Некоторые мои проекты хранятся в монорепо, тч для нормальной работы
ESLint потребовалась небольшая докрутка: parserOptions.project в .eslintrc.js поддерживает также массив, тч потребовалось внести каждое приложение, содержащее tsconfig.json отдельно.Также
vscode сразу не хотел обрабатывать нужные конфиги, не беда, в issue к плагину vscode-eslint предложили рабочее решение.P.S. Все же с
tslint-to-eslint-config некоторые правила останутся не перенесеными, тч смотрим также созланный тулзой файл tslint-to-eslint-config.log.#w_corner_ts
GitHub
GitHub - palantir/tslint: :vertical_traffic_light: An extensible linter for the TypeScript language
:vertical_traffic_light: An extensible linter for the TypeScript language - palantir/tslint
Второй подход к снаряду поиска типов
Сколько же решений по типизации экшенов выдает Stackoverflow, вплоть до пакетов с тремя звездочками на Github и каждый способ не лишен изъянов.
Возьмем например довольно очевидный способ создания экшена "в лоб":
И по общей договоренности с командой мы решили, что тип поля
И так, приступим исправлять типизацию.
Что же для нас такое этот
Это функция, принимающая
Чтобы это сделать мы воспользуемся таким вот хитрым хелпером, который позволит доказать, что тип действительно строка (если у вас есть идеи как это сделать проще - велком в личку).
Итоговый
P.S. В примерах не исключены ошибки ☺️
Всех с наступающим!🎄
#w_corner_ts
Сколько же решений по типизации экшенов выдает Stackoverflow, вплоть до пакетов с тремя звездочками на Github и каждый способ не лишен изъянов.
Возьмем например довольно очевидный способ создания экшена "в лоб":
interface Action<T, P> {
readonly type: T;
readonly payload?: P;
}
const createAction = <T extends string, P>(type: T) => (payload: P): Action<T, P> => {
return { type, payload };
}
В таком случае от нас TS будет требовать пробрасывать вторым аргументом в дженерик payload, что нам не всегда нужно (тк нельзя в дженерик с двумя аргументами передавать только один, ну и если попробуем от этого избавиться, то все равно в конце концов мы теряем тип от type).И по общей договоренности с командой мы решили, что тип поля
type обязательно должен быть строкой.И так, приступим исправлять типизацию.
Что же для нас такое этот
createAction в итоге. Это функция, принимающая
type и payload и возвращающая объект с этими двумя полями, а главное сохраняющая типы.const createAction = (type) => (payload) => ({
type,
payload,
})
И первый тип, с которым надо нам надо разобраться - это тип Action, содержащий payload, type и более базовый тип - ReduxAction (для него достаточно поля type)interface ReduxAction<T extends string> {
type: T
}
export interface Action<Type extends string, Payload = void> extends ReduxAction<Type> {
payload?: Payload
}
Тут все довольно просто. Обернем в это createAction.const createAction = <T extends string, P>(type: T) => (payload?: P): Action<T, P> => ({
type,
payload,
})
Чтож, мы пришли примерно к тому же, что и было в примере с SO. Только payload вне зависимости от экшена не обязательный, а типы этого поля так и не выводятся. Добавим магии lookup types и посмотрим, что у нас получится.export const createAction = <A extends Action<A['type'], A['payload']>>(type: A['type']) => (payload: A['payload']) => ({
type,
payload,
})
Теперь у нас выводится типы тайпсрипта по типу экшена. Так мы не можем доказать тайпчекеру, что тип у A['type'] - строка, и это не сработает (пример). Впрочем, мы и не пытались.Чтобы это сделать мы воспользуемся таким вот хитрым хелпером, который позволит доказать, что тип действительно строка (если у вас есть идеи как это сделать проще - велком в личку).
type StingTypeField = {
type: string
}
type LookupStringTypeField<T, K> = K extends keyof T ? (T extends StingTypeField ? T[K] : never) : never
В LookupStringTypeField мы говорим, что поле в объекте точно есть поле K и оно точно строка, а иначе и не может быть (never).Итоговый
createAction:const createAction = <A extends Action<LookupStringTypeField<A, 'type'>, A['payload']>>(type: A['type']) => (payload: A['payload']) => ({
type,
payload,
})
И конечно ссылка с примером.P.S. В примерах не исключены ошибки ☺️
Всех с наступающим!🎄
#w_corner_ts
www.typescriptlang.org
TS Playground - An online editor for exploring TypeScript and JavaScript
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
Небольшой пост по безопасности на фронтенде, куда вообще смотреть? Заметил, что мало кто в этой теме разбирается даже на базовом уровне, а тема важная.
Основные вопросы:
Виды атак, какой вектор, какая цель?
XSS, CSRF, clickjacking.
Как защититься?
CORS + SOP, CSP, Cookie: secure, httpOnly, SameSite
https://application.security/free-application-security-training - в интерактивном режиме показывается как можно произвести атаку и как от нее защититься
https://developer.mozilla.org/ru/docs/Web/HTTP/CORS
https://developer.mozilla.org/ru/docs/Web/HTTP/CSP
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
https://developer.mozilla.org/ru/docs/Web/HTTP/Cookies#secure_(%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D1%8B%D0%B5)_%D0%B8_httponly_cookies
https://habr.com/ru/post/340146/ - про то, как работает JWT
#w_corner_secure
Основные вопросы:
Виды атак, какой вектор, какая цель?
XSS, CSRF, clickjacking.
Как защититься?
CORS + SOP, CSP, Cookie: secure, httpOnly, SameSite
https://application.security/free-application-security-training - в интерактивном режиме показывается как можно произвести атаку и как от нее защититься
https://developer.mozilla.org/ru/docs/Web/HTTP/CORS
https://developer.mozilla.org/ru/docs/Web/HTTP/CSP
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
https://developer.mozilla.org/ru/docs/Web/HTTP/Cookies#secure_(%D0%B1%D0%B5%D0%B7%D0%BE%D0%BF%D0%B0%D1%81%D0%BD%D1%8B%D0%B5)_%D0%B8_httponly_cookies
https://habr.com/ru/post/340146/ - про то, как работает JWT
#w_corner_secure
MSW
Часто вам приходилось разрабатывать интерфейс без готовой ручки на бэкенде? Все эти временные данные на уровне стора или Promise.resolve с данными на уровне сервисов/sdk и прочие подходы, обеспечивающие нам одни и теже данные. А писать e2e тесты, когда чтобы замокать результат запросов всегда приходилось придумывать свои велосипеды (да-да, е2е с фейковыми данными запросами ужас и моветон - мы всегда поднимаем весь стек и ждём, что нигде ничего не отвалится и нашей машинке хватит сил).
Хватит. Почти всегда для того, чтобы подсунуть фэйковые данные на уровне сети я выбирал nock, но время идёт, индустрия движется, а подход к моку сервера в тестах остаётся прежним. Только не с Mock Service Worker и вот вам несколько причин перейти на него:
1. Простая установка
2. Возможность запускать одни и те же обработчики и для серверных (например,в случае с SSR) и для клиентских тестов из коробки
3. Возможность делать более умные обработчики на этапе жизни «без нужной бэкендовой ручки», а затем переиспользовать их в тестах
4. Поддержка rest и grqphql
5. Максимально прозрачное поведение и простота отладки
6. Ваши обработчики похожи на реальные express мидлвары
Ставим пакет, запускаем
#w_corner_testing
Часто вам приходилось разрабатывать интерфейс без готовой ручки на бэкенде? Все эти временные данные на уровне стора или Promise.resolve с данными на уровне сервисов/sdk и прочие подходы, обеспечивающие нам одни и теже данные. А писать e2e тесты, когда чтобы замокать результат запросов всегда приходилось придумывать свои велосипеды (да-да, е2е с фейковыми данными запросами ужас и моветон - мы всегда поднимаем весь стек и ждём, что нигде ничего не отвалится и нашей машинке хватит сил).
Хватит. Почти всегда для того, чтобы подсунуть фэйковые данные на уровне сети я выбирал nock, но время идёт, индустрия движется, а подход к моку сервера в тестах остаётся прежним. Только не с Mock Service Worker и вот вам несколько причин перейти на него:
1. Простая установка
2. Возможность запускать одни и те же обработчики и для серверных (например,в случае с SSR) и для клиентских тестов из коробки
3. Возможность делать более умные обработчики на этапе жизни «без нужной бэкендовой ручки», а затем переиспользовать их в тестах
4. Поддержка rest и grqphql
5. Максимально прозрачное поведение и простота отладки
6. Ваши обработчики похожи на реальные express мидлвары
Ставим пакет, запускаем
npx msw init public/ --save и вперед. Не забываем читать доку, и экспериментировать, думаю, обязательно найдете больше юзкейсов для себя.#w_corner_testing
mswjs.io
Quick start
Add MSW to your project in under five minutes.
Да будет Typescript
С этого поста хочу начать последовательный цикл дипдайва в тайпскрипт, в самом широком смысле: от причин использования, простейших типов и их юзкейсов, до устройства системы типов, транспилятора, точек его расширения и всего вокруг этого чудесного инструмента.
Начнем с самого начала: с базовых типов, из которых выводится все остальное.
То, что TypeScript является типизированной надстройкой над JavaScript, от которой после компиляции не остаётся и следа, означает, что первый перенял от второго всю его идеологию. Одним из таких моментов является разделение типов данных на типы значения (примитивные) и ссылочные типы.
Корнем всей иерархии типов можно считать unknown, который является типобезопасным аналогом any. Все типы совместимы с типом unknown, в, то время как сам тип unknown совместим только с самим собой и типом any.
Произвольный тип any — с него для многих и начнется знакомство с системой типов TypeScript.
Далее идут простые примитивные типы, имеющие свои аналоги в JavaScript: number, string, boolean, bigint, symbol. Важно понимать, что типы, идентификаторы которых начинаются с прописной буквы представляют объектные типы (ссылочные типы) описывающие одноимённые типы из JavaScript (Number, String, Boolean и т.д.). В Typescript их можно расширить (через extends) и реализовать (через implements).
В отдельную категорию можно вынести примитивные типы null, undefined, void, never, считая их в некотором роде «типами отсутствия».
Enum - перечисление также считается примитивным типом.
Для каждого из этих типов есть литеральный подтип, которые, как можно понять из названия, представляют литералы обычных примитивных типов.
#w_corner_ts
С этого поста хочу начать последовательный цикл дипдайва в тайпскрипт, в самом широком смысле: от причин использования, простейших типов и их юзкейсов, до устройства системы типов, транспилятора, точек его расширения и всего вокруг этого чудесного инструмента.
Начнем с самого начала: с базовых типов, из которых выводится все остальное.
То, что TypeScript является типизированной надстройкой над JavaScript, от которой после компиляции не остаётся и следа, означает, что первый перенял от второго всю его идеологию. Одним из таких моментов является разделение типов данных на типы значения (примитивные) и ссылочные типы.
Корнем всей иерархии типов можно считать unknown, который является типобезопасным аналогом any. Все типы совместимы с типом unknown, в, то время как сам тип unknown совместим только с самим собой и типом any.
Произвольный тип any — с него для многих и начнется знакомство с системой типов TypeScript.
Далее идут простые примитивные типы, имеющие свои аналоги в JavaScript: number, string, boolean, bigint, symbol. Важно понимать, что типы, идентификаторы которых начинаются с прописной буквы представляют объектные типы (ссылочные типы) описывающие одноимённые типы из JavaScript (Number, String, Boolean и т.д.). В Typescript их можно расширить (через extends) и реализовать (через implements).
В отдельную категорию можно вынести примитивные типы null, undefined, void, never, считая их в некотором роде «типами отсутствия».
Enum - перечисление также считается примитивным типом.
Для каждого из этих типов есть литеральный подтип, которые, как можно понять из названия, представляют литералы обычных примитивных типов.
#w_corner_ts
React Testing Library и Enzyme
Все больше и больше проектов уходят с Enzyme и переходят на RTL. Про это сейчас пишут статьи, делают доклады и даже оффициальная дока Recat рекомендует React Testing Library. Чем же так не угодил Enzyme и почему от него стоит отказаться.
1. Он завязан на кишки React, а значит очередное обновление ломает обратную совместимость и требует обновление Enzyme (а также вероятно написанных тестов)
2. Enzyme родился в Airbnb, но сейчас фактически поддерживается одним человеком.
3. Enzyme периодически вынуждает тестировать реализацию как «белый ящик», что также делает тесты более хрупкими.
Как начать писать тесты с RTL? Лично я рекомендую для старта почитать документацию, посмотреть мой доклад и этот плейлист. Для более заинтересованных в тестах также не могу оставить без внимания интенсив Hexlet по тестированию фронтенда.
#w_corner_testing
Все больше и больше проектов уходят с Enzyme и переходят на RTL. Про это сейчас пишут статьи, делают доклады и даже оффициальная дока Recat рекомендует React Testing Library. Чем же так не угодил Enzyme и почему от него стоит отказаться.
1. Он завязан на кишки React, а значит очередное обновление ломает обратную совместимость и требует обновление Enzyme (а также вероятно написанных тестов)
2. Enzyme родился в Airbnb, но сейчас фактически поддерживается одним человеком.
3. Enzyme периодически вынуждает тестировать реализацию как «белый ящик», что также делает тесты более хрупкими.
Как начать писать тесты с RTL? Лично я рекомендую для старта почитать документацию, посмотреть мой доклад и этот плейлист. Для более заинтересованных в тестах также не могу оставить без внимания интенсив Hexlet по тестированию фронтенда.
#w_corner_testing
piotrstaniow.pl
Time to say goodbye - Enzyme.js
The world has already moved - to React Testing Library
Заметки
идеальная система не та, в которую уже нечего добавить, а та, из которой уже нечего убрать
За последние лет 8 я попробовал минимум 30 приложений для ведения заметок, дел, чеклистов, WTR, финансов и прочих потоков информации, которые хочу структурировать.
А хотелось структурировать все. GTD для всех дел. Цеттелькастен для всех заметок. Выжать максимум из приложений для финансов. Еженедельная очистка want to read инбокса.
Косу нужно было идеально заточить, чтобы косить.
В результате вместо того, чтобы выполнять задуманную работу, куча времени уходило на поиск и освоение очередного разухабистого инструмента для GTD, обвес и настройка плагинами Obsidian’а, докрутка базы знаний в Notion. В конце концов полное отчаяние от того, что в итоге снова получилось слишком сложно.
Тогда я решил зайти с противоположного конца. Завел просто файлик в облаке.
В него писал короткий список с перечислением проектов, над которыми я фокусируюсь в текущую неделю. Сразу под этим списком вписывал все дни недели, а каждый вечер писал список из нескольких пунктов под следующим днем. Тоже самое сделал с финансами.
Через несколько недель такого упрощения я понял, какие именно элементы в моей системе продуктивности были лишние, избавился от них и смог вернуться в Things3.
Так же поступил с моим Obsidian. Создал новый сейф, избавился от всех лишних плагинов и сложных структур в заметках, оставил только самое необходимое.
В итоге пришло осознание, что для Zettelkasten'а достаточно папки с текстовыми файлами, куда я записываю сформировавшиеся мысли и иногда перечитываю их.
Для написания больших текстов вернулся к org-моду emacs.
Итогом пусть будет мысль, что практика - хорошо, а паралич выбора и FOMO - плохо. Надеюсь этот виток ведения дел окажется более удачным.
идеальная система не та, в которую уже нечего добавить, а та, из которой уже нечего убрать
За последние лет 8 я попробовал минимум 30 приложений для ведения заметок, дел, чеклистов, WTR, финансов и прочих потоков информации, которые хочу структурировать.
А хотелось структурировать все. GTD для всех дел. Цеттелькастен для всех заметок. Выжать максимум из приложений для финансов. Еженедельная очистка want to read инбокса.
Косу нужно было идеально заточить, чтобы косить.
В результате вместо того, чтобы выполнять задуманную работу, куча времени уходило на поиск и освоение очередного разухабистого инструмента для GTD, обвес и настройка плагинами Obsidian’а, докрутка базы знаний в Notion. В конце концов полное отчаяние от того, что в итоге снова получилось слишком сложно.
Тогда я решил зайти с противоположного конца. Завел просто файлик в облаке.
В него писал короткий список с перечислением проектов, над которыми я фокусируюсь в текущую неделю. Сразу под этим списком вписывал все дни недели, а каждый вечер писал список из нескольких пунктов под следующим днем. Тоже самое сделал с финансами.
Через несколько недель такого упрощения я понял, какие именно элементы в моей системе продуктивности были лишние, избавился от них и смог вернуться в Things3.
Так же поступил с моим Obsidian. Создал новый сейф, избавился от всех лишних плагинов и сложных структур в заметках, оставил только самое необходимое.
В итоге пришло осознание, что для Zettelkasten'а достаточно папки с текстовыми файлами, куда я записываю сформировавшиеся мысли и иногда перечитываю их.
Для написания больших текстов вернулся к org-моду emacs.
Итогом пусть будет мысль, что практика - хорошо, а паралич выбора и FOMO - плохо. Надеюсь этот виток ведения дел окажется более удачным.
🔥3❤🔥1
Type Programming
В мире Тайпскрипта большое внимание уделяется системе типов.
Типами мы пытаемся описать предметную область: заказы, магазин, пользователь, корзина.
Это достигается простейшим описанием объектного типа или интерфейса. Куда реже мы переопределяем стандартные типы, вроде чисел и превращаем их в брендированные типы - другой мощный механизм, ставший возможным благодаря широким возможностям системы типов.
Как правило на этих возможностях использование системы типов и заканчивается. В прикладном коде мы стараемся не описывать сложное взаимодействие бизнес сущностей через систему типов. Цена этого довольно велика, а выхлоп туманный.
Другая сторона медали - системный код: библиотеки, вспомогательные утилиты, абстрагирующие от сложной системы функции, методы и классы. Хороший слой абстракции должен максимально отдалять пользователей наших системных модулей от деталей реализации. У них не должно возникать желания заглянуть за кулисы и расширить под свои требования переписывая наш прекрасный код. Для этого наш код должен быть достаточно надежным, чтобы попадать под ограничения наших возможностей, и гибким - накладывать только необходимые ограничения для пространства маневра в бизнес-коде.
Чтобы решить подобные вопросы в “классических ООП” языках были выделено множество паттернов, которые заставляют стелить соломку абсолютно везде - стратегия на интерфейсе абстрактного класса, не должно быть классов не имплементирующих какой-то интерфейс. И прочие прелестные Джава-чсв “из умных книжек” догмы ака паттерны. А все изначально от ограничений. Пока не завезли дженерики даже достаточно типобезопасной функции мап было не сделать. Првильно, стратегии. Привет, любители Го.
В языках с динамической типизацией отродясь таких проблем не было. Они не задают совершенно никаких ограничений. Действительно хорошие попытки задать подобные ограничения в динамических языках - контрактное программирование, но лично я встречал его только на слайдах. Реальных ограничений в них нет. Что сейчас и привело к различным гибридным подходам к статической системе типов.
На другой чаше весов замечательные языки для “небожителей” аля Хаскель, Идрисы и прочие Окамелы. Из более хайпящих сейчас - Раст. Ух сколько радостей и головоломок приносят типы для тех, кто пишет на них библиотеки. И сколько бы я их не пробовал везде одна проблема - система типов сильно проникает в бизнесовый код. Реальный мир вошел в радужное царство единорогов-математиков. Как итог вместо фабрик, синглтонов и прочего из банды четырех мы получаем монады, монады-трасформеры и прочие монады-состояния. (тут важно разделить ФП и программирование на типах - не путаем)
И тут у Тайпскрипта неплохая позиция. Да, для типичных пользователей это все еще часто недосягаемый уровень. Как запрограммировать обход структуры на системе типов или даже написать обобщенный класс - загадка для многих, решаемая только чат гопотой или везением со SO. Но сама возможность прекрасна и при достаточном желании можно даже писать так, чтобы типы эти не лезли в бизнес слой и ребята продолжали описывать простейшие интерфейсики и объектные типы.
А для тех, кто решит преисполниться в программировании на типах могу порекомендовать курс https://type-level-typescript.com - без такого введение в программирование на типах входить было куда сложнее. Только как войдете помните пожалуйста о цене программирования на типах и не заставляйте входить в этот мир других.
В мире Тайпскрипта большое внимание уделяется системе типов.
Типами мы пытаемся описать предметную область: заказы, магазин, пользователь, корзина.
Это достигается простейшим описанием объектного типа или интерфейса. Куда реже мы переопределяем стандартные типы, вроде чисел и превращаем их в брендированные типы - другой мощный механизм, ставший возможным благодаря широким возможностям системы типов.
Как правило на этих возможностях использование системы типов и заканчивается. В прикладном коде мы стараемся не описывать сложное взаимодействие бизнес сущностей через систему типов. Цена этого довольно велика, а выхлоп туманный.
Другая сторона медали - системный код: библиотеки, вспомогательные утилиты, абстрагирующие от сложной системы функции, методы и классы. Хороший слой абстракции должен максимально отдалять пользователей наших системных модулей от деталей реализации. У них не должно возникать желания заглянуть за кулисы и расширить под свои требования переписывая наш прекрасный код. Для этого наш код должен быть достаточно надежным, чтобы попадать под ограничения наших возможностей, и гибким - накладывать только необходимые ограничения для пространства маневра в бизнес-коде.
Чтобы решить подобные вопросы в “классических ООП” языках были выделено множество паттернов, которые заставляют стелить соломку абсолютно везде - стратегия на интерфейсе абстрактного класса, не должно быть классов не имплементирующих какой-то интерфейс. И прочие прелестные Джава-чсв “из умных книжек” догмы ака паттерны. А все изначально от ограничений. Пока не завезли дженерики даже достаточно типобезопасной функции мап было не сделать. Првильно, стратегии. Привет, любители Го.
В языках с динамической типизацией отродясь таких проблем не было. Они не задают совершенно никаких ограничений. Действительно хорошие попытки задать подобные ограничения в динамических языках - контрактное программирование, но лично я встречал его только на слайдах. Реальных ограничений в них нет. Что сейчас и привело к различным гибридным подходам к статической системе типов.
На другой чаше весов замечательные языки для “небожителей” аля Хаскель, Идрисы и прочие Окамелы. Из более хайпящих сейчас - Раст. Ух сколько радостей и головоломок приносят типы для тех, кто пишет на них библиотеки. И сколько бы я их не пробовал везде одна проблема - система типов сильно проникает в бизнесовый код. Реальный мир вошел в радужное царство единорогов-математиков. Как итог вместо фабрик, синглтонов и прочего из банды четырех мы получаем монады, монады-трасформеры и прочие монады-состояния. (тут важно разделить ФП и программирование на типах - не путаем)
И тут у Тайпскрипта неплохая позиция. Да, для типичных пользователей это все еще часто недосягаемый уровень. Как запрограммировать обход структуры на системе типов или даже написать обобщенный класс - загадка для многих, решаемая только чат гопотой или везением со SO. Но сама возможность прекрасна и при достаточном желании можно даже писать так, чтобы типы эти не лезли в бизнес слой и ребята продолжали описывать простейшие интерфейсики и объектные типы.
А для тех, кто решит преисполниться в программировании на типах могу порекомендовать курс https://type-level-typescript.com - без такого введение в программирование на типах входить было куда сложнее. Только как войдете помните пожалуйста о цене программирования на типах и не заставляйте входить в этот мир других.
Type-Level TypeScript
An online course to become a TypeScript expert. Discover the most advanced features of the type system while solving fun challenges!
🔥3🦄2😱1
Top-down и bottom-up команды
Два разных подхода: продумать и сделать или сесть и начать с экспериментов. И быть готовым выкинуть несколько прототипов.
Оба подхода имеют право на жизнь. В одном мы силами системных архитекторов и аналитиков проектируем систему и потом делаем, а в другом мы должны что-то постоянно пробовать делать.
Провальный подход карго-культа на проектирование классно описан в книге про Agile (про разработку системы для полиции), а провалы вторых можно наблюдать в каждом третьем стартапе и сломанных ad-hoc процессах в ваших компаниях.
Есть такое предположение, что в одной тесно интегрированной команде ужиться тем и другим будет невозможно, но хорошо работает синергия таких команд в проекте.
Хорошо - когда ты понимаешь какая команда у вас и как вам комфортно двигаться вместе. Плохо - "ломать людей" и навязывать свой путь решения проблем.
Два разных подхода: продумать и сделать или сесть и начать с экспериментов. И быть готовым выкинуть несколько прототипов.
Оба подхода имеют право на жизнь. В одном мы силами системных архитекторов и аналитиков проектируем систему и потом делаем, а в другом мы должны что-то постоянно пробовать делать.
Провальный подход карго-культа на проектирование классно описан в книге про Agile (про разработку системы для полиции), а провалы вторых можно наблюдать в каждом третьем стартапе и сломанных ad-hoc процессах в ваших компаниях.
Есть такое предположение, что в одной тесно интегрированной команде ужиться тем и другим будет невозможно, но хорошо работает синергия таких команд в проекте.
Хорошо - когда ты понимаешь какая команда у вас и как вам комфортно двигаться вместе. Плохо - "ломать людей" и навязывать свой путь решения проблем.
🔥3
FSMoothy
Сходил на Kolesa.conf. И тут подъехали видео докладов.
Самой мякоткой для меня, конечно, был доклад про конечные автоматы.
Когда узнал, что Кирилл собрался делать доклад на тему КА - я поднапрягся и доделал свою либу для TypeORM (typeorm-fsm). До того, как в проектах мы переехали на нее использовали какой-то огрызок с самописной обвязкой. Тут же я приложил чуть больше своего api-дизайнерского скилла и вроде даже получилось скрестить суть-фундамент с практической пользой.
Как мне кажется, основой такой библиотеки в приложении к самомой логике автоматов должны служить два подхода: Observer (реакция на события), DI (использование внешних систем на переходах).
Пробывал ли я еще что-то? Да, посмотрел много вариантов в npm. Везде свои трейдофы. На фронтовом проекте я ранее применял XState. Давайте рассмотрим что меня в нем не устроило:
- Портянка для описания в виде js объекта. Переходы описываются вербозно (зато есть визуализатор), поддерживает стандарт State Chart'ов.
- Observer на action'ах. Описывается очень вербозно. В разных частях портянки объекта-описания.
- Активный конечный автомат (actor). Круто! Но, в таком виде на бэке скорее очень мало куда подойдет.
- Персист данных встроенный в саму либу. На самом же деле логика персиста ложится на клиентскую часть. В экосистеме готового ничего нет.
- Своя система работы с эффектами. Выглядит прикольно, но на деле в таком виде нужно только для активного конечного автомата.
Есть более простые реализации конечных автоматов. Но в них всегда не хватает каких-то фич, которые фундаментальные, а без них некоторые кейсы решать очень сложно:
- Гварды. Условия на переходах. Без них никуда. Иначе логика также размажется по коду.
- Не все события можно отследить. В моем понимании у события есть следующий следующий жизненный цикл: покидаем предыдущее состояние (onLeave), входим в новое (onEnter), меняем состояние (transition), сработали глобальные подписчики, окончание перехода (onExit). Отсутсвие части этих подписчиков также заставляет размазать часть логики из перехода в клиентский код.
- Вложенные и паралельные состояния. Когда горит красный сигнал светофора - может загореться зеленный сигнал для пешехода. Согласование состояния - одна из фундаментальных фишек автомата. Мы можем сделать отдельное состояние для этого случая или сделать вложенный автомат для состояния "красный сигнал" у светофора. Наличие этой фичи решает проблему роста автомата (state explosion problem).
- Вложенные данные. Автоматы также могут содержать дополнительные атрибуты. Например, счетчик времени. И использовать их на переходах.
- Асинхронные подгрузки. Может потребоваться подгружать начальное состояние и вложенные данные из внешних источников.
...
- и на самом деле много чего еще
Моим решением стало написание собственной либы - fsmoothy. В ней все описанные вещи заложены в дизайн и уже реализованны. Дальнейшее расширение планируется за счет написания отдельных пакетов поверх нее и расширения экосистемы.
Выделяйте автоматы у себя в коде явно, думайте о них при проектировании. Берегите свою кодовую базу!💎
Сходил на Kolesa.conf. И тут подъехали видео докладов.
Самой мякоткой для меня, конечно, был доклад про конечные автоматы.
Когда узнал, что Кирилл собрался делать доклад на тему КА - я поднапрягся и доделал свою либу для TypeORM (typeorm-fsm). До того, как в проектах мы переехали на нее использовали какой-то огрызок с самописной обвязкой. Тут же я приложил чуть больше своего api-дизайнерского скилла и вроде даже получилось скрестить суть-фундамент с практической пользой.
Как мне кажется, основой такой библиотеки в приложении к самомой логике автоматов должны служить два подхода: Observer (реакция на события), DI (использование внешних систем на переходах).
Пробывал ли я еще что-то? Да, посмотрел много вариантов в npm. Везде свои трейдофы. На фронтовом проекте я ранее применял XState. Давайте рассмотрим что меня в нем не устроило:
- Портянка для описания в виде js объекта. Переходы описываются вербозно (зато есть визуализатор), поддерживает стандарт State Chart'ов.
- Observer на action'ах. Описывается очень вербозно. В разных частях портянки объекта-описания.
- Активный конечный автомат (actor). Круто! Но, в таком виде на бэке скорее очень мало куда подойдет.
- Персист данных встроенный в саму либу. На самом же деле логика персиста ложится на клиентскую часть. В экосистеме готового ничего нет.
- Своя система работы с эффектами. Выглядит прикольно, но на деле в таком виде нужно только для активного конечного автомата.
Есть более простые реализации конечных автоматов. Но в них всегда не хватает каких-то фич, которые фундаментальные, а без них некоторые кейсы решать очень сложно:
- Гварды. Условия на переходах. Без них никуда. Иначе логика также размажется по коду.
- Не все события можно отследить. В моем понимании у события есть следующий следующий жизненный цикл: покидаем предыдущее состояние (onLeave), входим в новое (onEnter), меняем состояние (transition), сработали глобальные подписчики, окончание перехода (onExit). Отсутсвие части этих подписчиков также заставляет размазать часть логики из перехода в клиентский код.
- Вложенные и паралельные состояния. Когда горит красный сигнал светофора - может загореться зеленный сигнал для пешехода. Согласование состояния - одна из фундаментальных фишек автомата. Мы можем сделать отдельное состояние для этого случая или сделать вложенный автомат для состояния "красный сигнал" у светофора. Наличие этой фичи решает проблему роста автомата (state explosion problem).
- Вложенные данные. Автоматы также могут содержать дополнительные атрибуты. Например, счетчик времени. И использовать их на переходах.
- Асинхронные подгрузки. Может потребоваться подгружать начальное состояние и вложенные данные из внешних источников.
...
- и на самом деле много чего еще
Моим решением стало написание собственной либы - fsmoothy. В ней все описанные вещи заложены в дизайн и уже реализованны. Дальнейшее расширение планируется за счет написания отдельных пакетов поверх нее и расширения экосистемы.
Выделяйте автоматы у себя в коде явно, думайте о них при проектировании. Берегите свою кодовую базу!
Please open Telegram to view this post
VIEW IN TELEGRAM
YouTube
Кирилл Мокевнин, Конечные автоматы как способ значительно упростить логику и понимание кода
Kolesa Conf'23, доклад
В этом докладе мы увидим, что практически любая бизнес-логика в коде укладывается в модель конечного автомата, то есть состоит из набора состояний и переходов между ними. Такой взгляд на происходящее позволит изменить подход к проектированию…
В этом докладе мы увидим, что практически любая бизнес-логика в коде укладывается в модель конечного автомата, то есть состоит из набора состояний и переходов между ними. Такой взгляд на происходящее позволит изменить подход к проектированию…
🔥7👍1🦄1
Стримы
В последнее время я регулярно стримлю на канал MemeBattle. У нас кончились кредиты на AWS и мы в рамках стримов сейчас переезжаем на Яндекс Клауд.
Но это не все темы для стримов. Недавно закончился Advent of Typescript и мы совместно с @xufostation и другими ребятами, которые прошли весь этот челендж, застримили решение почти всех задачек с довольно подробным разбором. В итоге получился такой мастер класс по Type Level программированию. Думаю, те, кто захочет повторить и послушать как решать такие задачки вынесут много полезного.
В планах на следующие стримы продолжить переписывать одни из наших сервисов для хранения истории Ligretto игр на Platformatic (что это и зачем можно также узнать из стримов), переписать несколько babel плагинов на swc на Rust'е и много чего еще полезного.
В общем, агитирую подписаться на наш канал, ставить лайки, жать на колокольчик чтобы первыми узнавать о предстоящих стримах и конечно же приходить нас смотреть!
https://www.youtube.com/@memebattle_dev/streams
В последнее время я регулярно стримлю на канал MemeBattle. У нас кончились кредиты на AWS и мы в рамках стримов сейчас переезжаем на Яндекс Клауд.
Но это не все темы для стримов. Недавно закончился Advent of Typescript и мы совместно с @xufostation и другими ребятами, которые прошли весь этот челендж, застримили решение почти всех задачек с довольно подробным разбором. В итоге получился такой мастер класс по Type Level программированию. Думаю, те, кто захочет повторить и послушать как решать такие задачки вынесут много полезного.
В планах на следующие стримы продолжить переписывать одни из наших сервисов для хранения истории Ligretto игр на Platformatic (что это и зачем можно также узнать из стримов), переписать несколько babel плагинов на swc на Rust'е и много чего еще полезного.
В общем, агитирую подписаться на наш канал, ставить лайки, жать на колокольчик чтобы первыми узнавать о предстоящих стримах и конечно же приходить нас смотреть!
https://www.youtube.com/@memebattle_dev/streams
🔥8
Двадцать дней как состоялся релиз первой версии языка Gleam, который должен ознаменовать стабильность и готовность языка к продакшен моментам. И двадцать дней как я пробую этот язык с разных сторон. и мне зашло
Давайте посмотрим, что включено в первый релиз и отмечено core-командой как стабильное:
⭐️ Дизайн языка. Все четко, язык похож на Rust+Elixir, но максимально минималистичнее - я бы сказал, что даже минималистичнее Go
⭐️ Компилятор. Тут тоже есть чем удивить: реализация TCO для JS, довольно понятный как Erlang, так и JS код на выходе. Написан на Rust’е, так что все работает быстро
⭐️ Build Tool. За то время, что пользуюсь ощущение, примерно как от Cargo, четенько
⭐️ Форматирование. Немного вкусовщины, но в целом все по кайфу
⭐️ Языковой сервер. Пока самая слабенькая часть. Нет, например, подсказок по маркированным(labeled) аргументам, автодополнение довольно глуповатое, не всегда ошибки там где надо показываются и еще пяток мелочей. Но в репозитории языка висит довольно много PR’ов улучшающих LSP.
⭐️ Компилятор в WASM и JS-биндинги. JS-биндинги имеют свой вполне очевидный рантайм оверхед. Прелюдия не большая, что в простых примерах ворочающих списки дает меньший выхлоп в бандл чем тот же ReScript.
Экосистема языка показалась мне не большой, но все минимальное для вебчика уже есть. wisp в качестве минималистичного web-фреймворка. lustre - elm-like фронтенд фреймворк. Есть свой индекс для пакетов - можно посмотреть что вообще есть.
Сам Gleam в первую очередь направлен хоститься на виртуальной машине Erlang - BEAM, что довольно четко очерчивает границы применимости и нишевость языка. Однако хороший интероп в JS с простым FFI и без расскраски функций - сильно расширяют его возможности.
Если вас заинтересовало - для быстрого ознакомления есть тур на оффициальном сайте, а чтобы попрактиковаться есть бесплатный трек с обучением на Exercism.
Давайте посмотрим, что включено в первый релиз и отмечено core-командой как стабильное:
Экосистема языка показалась мне не большой, но все минимальное для вебчика уже есть. wisp в качестве минималистичного web-фреймворка. lustre - elm-like фронтенд фреймворк. Есть свой индекс для пакетов - можно посмотреть что вообще есть.
Сам Gleam в первую очередь направлен хоститься на виртуальной машине Erlang - BEAM, что довольно четко очерчивает границы применимости и нишевость языка. Однако хороший интероп в JS с простым FFI и без расскраски функций - сильно расширяют его возможности.
Если вас заинтересовало - для быстрого ознакомления есть тур на оффициальном сайте, а чтобы попрактиковаться есть бесплатный трек с обучением на Exercism.
Please open Telegram to view this post
VIEW IN TELEGRAM
gleam.run
Gleam version 1 | Gleam programming language
News post: It's finally here! 🎉
🦄7👍5 2
Провел сегодня две замечательные сессии на TechTrain в роли эксперта, что поставило окончательную точку после трех месяцев подготовки в роли члена программного комитета. Время пролетело незаметно и как по мне, так вышло очень хорошо.
Мне в целом, нравится помогать готовиться ребятам и наблюдать за тем, что у них получается в итоге. Утренний доклад с Давидом из @it_kachalka про ООП однозначно рекомендую всем к просмотру! Прям хорошая рефлексия и выводы, которые привели его к Функционально Ориентированному Программированию🦀. Дискуссия в конце вообще безумно зарядила меня на ближайшее время.
Если у вас есть что рассказать и вы еще думаете выступать или нет - выступайте. Если думаете о том, стоит ли ходить смотреть доклады - ходите. Ведь митапы, пусть и онлайн - это даже не столько доклады, где вы что-то новое узнаете. Но и тусовка, где можно обсудить свои боли и идеи. Я лично всегда очень тепло вспоминаю, как у нас проходили митапы в Новосибирске, те кулуарные темы и крутые движухи, которые мы устраивали после мероприятий с друзьями.🌈
Мне в целом, нравится помогать готовиться ребятам и наблюдать за тем, что у них получается в итоге. Утренний доклад с Давидом из @it_kachalka про ООП однозначно рекомендую всем к просмотру! Прям хорошая рефлексия и выводы, которые привели его к Функционально Ориентированному Программированию🦀. Дискуссия в конце вообще безумно зарядила меня на ближайшее время.
Если у вас есть что рассказать и вы еще думаете выступать или нет - выступайте. Если думаете о том, стоит ли ходить смотреть доклады - ходите. Ведь митапы, пусть и онлайн - это даже не столько доклады, где вы что-то новое узнаете. Но и тусовка, где можно обсудить свои боли и идеи. Я лично всегда очень тепло вспоминаю, как у нас проходили митапы в Новосибирске, те кулуарные темы и крутые движухи, которые мы устраивали после мероприятий с друзьями.
Please open Telegram to view this post
VIEW IN TELEGRAM
TechTrain 2024 Spring. Фестиваль по профессиональному росту в IT
ООП: от любви до ненависти — один шаг. Но куда? | Доклад на TechTrain 2024 Spring
О моем пути осознания, что ООП применимо только в узких областях и приносит больше проблем, чем пользы в мультипарадигмальном программировании. Как, объединив лучшие практики функционального и процедурного программирования, я пришел к ФОП (функционально …
❤🔥3🔥2🦄2 2
Continuations
Один из моих любимых топиков в программировании - различные способы зафиксировать выполнение процесса программы на определенном месте и затем вернуться к нему. Это также называют Continuation. Впервые возможности которые за этим скрыты меня впечатлили когда я погружался в генераторы и в реализацию Redux Sagas. И теперь в каждом новом языке ищу нечто подобное и нахожу новые возможности, как это можно применять.
В языках, с которыми я сталкивался чаще всего есть одна из форм сontinuation -
Кроме раскраски функций типичный сontinuation в разных языках еще и не сериализуем, а значит не получится продолжить выполнение на другой машине, что вместе с потребляемыми CPU ресурсами ставит предел применимости этой техники.
И тут для меня на сцену вышла виртуальная машина Erlang - Beam. Процессы в Beam легковесны, планировщик встроенный в VM переключает работу с зависших процессов на живые и позволяет их распределять по всем инстансам VM, которые могут находиться на разных тачках. И вооружившись Gleam’ом я попробовал решить одну классическую проблему с Telegram ботами.
Как правило, чтобы имитировать последовательное общение между чат ботом и пользователем нам нужно куда-то сохранять состояние чата с текущим пользователем. И уже на следующий ответ снова запускать всю цепочку обработчиков и проверять состояние чата. Тут в реализации начинают появляться конечные автоматы или всякие специализированные подходы, вроде сцен.
Но по своей природе это задача как раз для континуаций. И тут на сцену выходит Conversations в grammy. Где вы просто пишете
Что собственно я и сделал - реализовал аналог Conversation API, но уже на Gleam. Вы уже можете попробовать сделать на этом простых чат-ботов (я +- стабилизировал внешний интерфейс, так что вряд ли что-то большое сломаю). Тут нет покраски функций, нет проблем с сериализацией и распределением между инстансами и как мне кажется довольно просто начать это писать (и читать):
Больше примеров можно посмотреть тут и тут!
В итоге писать чат ботов выходит куда более естественно, чем с различными конечными автоматам. Пробуйте, может вам тоже понравится!
Один из моих любимых топиков в программировании - различные способы зафиксировать выполнение процесса программы на определенном месте и затем вернуться к нему. Это также называют Continuation. Впервые возможности которые за этим скрыты меня впечатлили когда я погружался в генераторы и в реализацию Redux Sagas. И теперь в каждом новом языке ищу нечто подобное и нахожу новые возможности, как это можно применять.
В языках, с которыми я сталкивался чаще всего есть одна из форм сontinuation -
async/await. Чаще всего мы маркируем функции как async и чтобы зафиксировать состояние программы и отдать управление куда-то еще используем await. Такая маркировка еще называется раскраской функций. Однако, этот подход требует сложной поддержки всего этого дела со стороны рантайма, например, в Rust для этого вообще много интересной низкоуровневой возьни - посмотрите на то как там работает Tokio и какие в нем используются примитивы. Генераторы примерно туда же, хотя они как правило дают чуть больше контроля над процессом выполнения, но с ними все те же проблемы.Кроме раскраски функций типичный сontinuation в разных языках еще и не сериализуем, а значит не получится продолжить выполнение на другой машине, что вместе с потребляемыми CPU ресурсами ставит предел применимости этой техники.
И тут для меня на сцену вышла виртуальная машина Erlang - Beam. Процессы в Beam легковесны, планировщик встроенный в VM переключает работу с зависших процессов на живые и позволяет их распределять по всем инстансам VM, которые могут находиться на разных тачках. И вооружившись Gleam’ом я попробовал решить одну классическую проблему с Telegram ботами.
Как правило, чтобы имитировать последовательное общение между чат ботом и пользователем нам нужно куда-то сохранять состояние чата с текущим пользователем. И уже на следующий ответ снова запускать всю цепочку обработчиков и проверять состояние чата. Тут в реализации начинают появляться конечные автоматы или всякие специализированные подходы, вроде сцен.
Но по своей природе это задача как раз для континуаций. И тут на сцену выходит Conversations в grammy. Где вы просто пишете
const newContext = await conversation.wait(); и оно работает. Но тут все те же проблемы сontinuation’ов и еще немного специфичных ограничений из-за деталей реализации. И Beam это может помочь решить. Что собственно я и сделал - реализовал аналог Conversation API, но уже на Gleam. Вы уже можете попробовать сделать на этом простых чат-ботов (я +- стабилизировал внешний интерфейс, так что вряд ли что-то большое сломаю). Тут нет покраски функций, нет проблем с сериализацией и распределением между инстансами и как мне кажется довольно просто начать это писать (и читать):
fn set_name_command_handler(
ctx: BotContext,
_,
) -> Result(NameBotSession, String) {
use <- telega.log_context(ctx, "set_name command")
use _ <- result.try(telega_api.reply(ctx, "What's your name?"))
use ctx, name <- telega.wait_text(ctx)
use _ <- result.try(telega_api.reply(ctx, "Your name is: " <> name <> " set!"))
Ok(NameBotSession(name: name))
}
Больше примеров можно посмотреть тут и тут!
В итоге писать чат ботов выходит куда более естественно, чем с различными конечными автоматам. Пробуйте, может вам тоже понравится!
😱6
Forwarded from igrishaev
Когда неподготовленный человек видит Лисп, он как-то реагирует: хихикает, лепит эмодзи, вовлекает других, словом — переживает. В такую минуту он напоминает школьника, который принес эротический журнал: смотрит на груди и попы, конфузится, краснеет, показывает другим под партой. Вроде бы интересно, но что с этим делать — не понятно.
Хорошо, если бы программистам объяснили: Лисп — лучший способ записать код. Любой язык можно улучшить хотя бы тем, что сделать синтаксис лиспо-подобным. Пусть даже парадигма останется прежней.
У скобочной записи есть преимущество: каждое выражение имеет начало и конец. Убедитесь, что прочли последнее предложение вдумчиво. В Лиспе каждая форма имеет начало и конец. В других языках — нет.
Предположим, я вижу выражение
Значит ли это, что выражение закончено? Конечно нет. За
Кроме того, что выражение определяется "на глазок", сюда вкрадывается приоритет операторов:
В то время на Лиспе первое выражение будет таким:
Скобки задают границы. Если скобка закрылась, то выражение закончилось, точка. Все, что следует дальше, относится ко внешнему выражению. Второе выражение сводит на нет котовасию с приоритетом операторов:
Все задано явно, вопросов быть не может. Вы, конечно, скажете, что приоритет умножения известен каждому школьнику? Тогда счастливой отладки дебажить что-то такое:
Из сказанного следует, что в Лиспе удобно работать с выражениями. Например, я могу выделить текущую форму. Обратите внимание — форму! Не метод, не сложение чисел, не класс, а именно форму! Потому что в Лиспе все это — форма. Мне не нужны хоткеи "Select method", "Select class", "Select whatever". Мне достаточно одной клавиши, чтобы покрыть все случаи.
Формы в Лиспе можно разбивать и объединять. Стоит нажать кнопку, и выражение
Форма может поглощать другие формы. Например, у меня есть код:
Теперь нужно, чтобы форма была внутри условия. Прямо над ней я пишу:
Далее, находясь внутри
Разумеется, есть другая кнопка, чтобы "выплюнуть" форму, и я получу то, что было до поглощения.
Каждый думает, что к нему это не относится, ведь он же пишет не на Лиспе. Но вот реальный пример на Джаве с цепочкой футур:
Каждый видит в меру своей испорченности, но я вижу здесь Лисп. Ему немного не повезло: нужно только переставить скобки, и получится нормально. Но вот курьез: Джава-человек в упор этого не видит. Для него это по-прежнему код на Джаве, а переставишь скобки — и все, ржака.
Теперь нужно изменить код так, чтобы
Хорошо, если бы программистам объяснили: Лисп — лучший способ записать код. Любой язык можно улучшить хотя бы тем, что сделать синтаксис лиспо-подобным. Пусть даже парадигма останется прежней.
У скобочной записи есть преимущество: каждое выражение имеет начало и конец. Убедитесь, что прочли последнее предложение вдумчиво. В Лиспе каждая форма имеет начало и конец. В других языках — нет.
Предположим, я вижу выражение
x = foo + barЗначит ли это, что выражение закончено? Конечно нет. За
bar вполне может быть продолжение:x = foo + bar * kek + lolКроме того, что выражение определяется "на глазок", сюда вкрадывается приоритет операторов:
bar * kek нельзя разорвать.В то время на Лиспе первое выражение будет таким:
(define x (+ foo bar))Скобки задают границы. Если скобка закрылась, то выражение закончилось, точка. Все, что следует дальше, относится ко внешнему выражению. Второе выражение сводит на нет котовасию с приоритетом операторов:
(define x (+ foo (* bar kek) lol))Все задано явно, вопросов быть не может. Вы, конечно, скажете, что приоритет умножения известен каждому школьнику? Тогда счастливой отладки дебажить что-то такое:
Some shit = foo && bar || test ^ foo;Из сказанного следует, что в Лиспе удобно работать с выражениями. Например, я могу выделить текущую форму. Обратите внимание — форму! Не метод, не сложение чисел, не класс, а именно форму! Потому что в Лиспе все это — форма. Мне не нужны хоткеи "Select method", "Select class", "Select whatever". Мне достаточно одной клавиши, чтобы покрыть все случаи.
Формы в Лиспе можно разбивать и объединять. Стоит нажать кнопку, и выражение
(+ foo bar) становится просто + foo bar. Далее я могу что-то сделать с его элементами. Форму можно двигать выше, ниже по текущему уровню вложенности. Можно втолкнуть ее внутрь. Можно вытолкнуть наверх из-под условия.Форма может поглощать другие формы. Например, у меня есть код:
(do-some-stuff x y z)Теперь нужно, чтобы форма была внутри условия. Прямо над ней я пишу:
(when some-condition)
(do-some-stuff x y z)Далее, находясь внутри
when, я жму кнопку, и форма втягивает в себя следующую за ней форму, и получается:(when some-condition
(do-some-stuff x y z))Разумеется, есть другая кнопка, чтобы "выплюнуть" форму, и я получу то, что было до поглощения.
Каждый думает, что к нему это не относится, ведь он же пишет не на Лиспе. Но вот реальный пример на Джаве с цепочкой футур:
return prepare(sql, executeParams)
.thenCompose((PreparedStatement stmt) -> sendBind(portal, stmt, executeParams))
.thenCompose((Integer ignored) -> sendDescribePortal(portal))
.thenCompose((Integer ignored) -> sendExecute(portal, executeParams.maxRows()))
.thenCompose((Integer ignored) -> sendClosePortal(portal))
.thenCompose((Integer ignored) -> sendCloseStatement(stmt))
.thenCompose((Integer ignored) -> sendSync())
.thenCompose((Integer ignored) -> sendFlush())
.thenCompose((Integer ignored) -> interact(executeParams))
.thenCompose((Result res) -> CompletableFuture.completedFuture(res.getResult()));Каждый видит в меру своей испорченности, но я вижу здесь Лисп. Ему немного не повезло: нужно только переставить скобки, и получится нормально. Но вот курьез: Джава-человек в упор этого не видит. Для него это по-прежнему код на Джаве, а переставишь скобки — и все, ржака.
Теперь нужно изменить код так, чтобы
stmt оставался в поле видимости на большее число шагов. Получится вот так:👍4❤🔥1🤨1 1
Малюткам DeFi-dev'ам. Часть 1.
Я успел немного копнуть в разработку DeFi. До этого я попрогал в целом для блокчейнов - смарт контакты, децентрализованное хранение (IPFS) и прочее всякое около. И был достаточно погружен в само явление DeFi как пользователь, холдер, ну и как многие пытался здесь навариться (лудка дело такое).
И если вы, как и я когда-то, заинтересовались программированием для DeFi и не знаете куда копать и с чего начать, то я надеюсь дать вам какую-то базу.
Первое, что стоит помнить и всегда держать в голове - в крипте очень много скама. Это благодатная почва как для лудоманов, так и для мошенников разных мастей (иногда они даже не осознают всю скам-природу их проекта). Идти сюда в надежде залутать шальные деньги - по сути тоже самое, что крутить слоты в надежде на драконий куш. Также слушать блогеров и прочих крипто-чуваков не стоит. Многие из них выезжают только за счет ставок против аудитории и инсайдов. Да и "большой" трейдинг в крипте держится на очень зыбких субстанциях.
Зато, на почве все еще зарождающегося рынка без внятного регулирования, находящейся до сих пор в серой зоне, каждый программист, которому интересна область финансов, может попробовать себя в деле и не прикасаться к реальным банкам с кучей бюрократии и сложностей.
По своей природе DeFi очень интересная область с точки зрения разработки. В ней сперва нашли себя шифропанки, затем стартаперы средней руки, а теперь и выросшие крипто-компании с десятками тысяч сотрудников. Если сейчас посмотреть сверху на рынок того, что тут делается, то за верхушкой айсберга крупных бирж скрывается много чего поменьше, и более интересного.
Вот такой список популярных видов проектов я накидал: AMM (автоматические маркет мейкеры), разного рода MEV автоматизация, анти-скам системы, лаунчпады, кредитование, управление активами, разные страховые системы, DEX'ы, DAO, Yield Farming, кроссчейн протоколы, NFT и прочие стартапчики использующие крипту как платежный шлюз.
В следующих частях обязательно рассмотрим каждый компонент DeFi с точки зрения потребителя и разработки в EVM и Solana сетях (может еще и Ton немного затронем, хотя тут пока с реальными DeFi проектами не густо).
Примерный список ключевых тем (на мой скромный взгляд) по DeFi: чем отличаются сети и чейны, обернутые токены, как запускаются токены, стейбл коины, пулл ликвидности, централизованные и децентрализованные платформы, автоматические маркет мейкеры, феномен Uniswap V3 и почему это круто, мемпулл, валидаторы, арбитраж, снайп и прочие боты (+ разные виды атак).
Что из того, что я перечислил выше вам знакомо? Что интересно в первую очередь?
Ну, и как мне кажется этих тем должно быть достаточно для старта DeFi-dev малютки. И, конечно, буду рад если мощные ребята докинут тем и помогут их разобрать.
Я успел немного копнуть в разработку DeFi. До этого я попрогал в целом для блокчейнов - смарт контакты, децентрализованное хранение (IPFS) и прочее всякое около. И был достаточно погружен в само явление DeFi как пользователь, холдер, ну и как многие пытался здесь навариться (лудка дело такое).
И если вы, как и я когда-то, заинтересовались программированием для DeFi и не знаете куда копать и с чего начать, то я надеюсь дать вам какую-то базу.
Первое, что стоит помнить и всегда держать в голове - в крипте очень много скама. Это благодатная почва как для лудоманов, так и для мошенников разных мастей (иногда они даже не осознают всю скам-природу их проекта). Идти сюда в надежде залутать шальные деньги - по сути тоже самое, что крутить слоты в надежде на драконий куш. Также слушать блогеров и прочих крипто-чуваков не стоит. Многие из них выезжают только за счет ставок против аудитории и инсайдов. Да и "большой" трейдинг в крипте держится на очень зыбких субстанциях.
Зато, на почве все еще зарождающегося рынка без внятного регулирования, находящейся до сих пор в серой зоне, каждый программист, которому интересна область финансов, может попробовать себя в деле и не прикасаться к реальным банкам с кучей бюрократии и сложностей.
По своей природе DeFi очень интересная область с точки зрения разработки. В ней сперва нашли себя шифропанки, затем стартаперы средней руки, а теперь и выросшие крипто-компании с десятками тысяч сотрудников. Если сейчас посмотреть сверху на рынок того, что тут делается, то за верхушкой айсберга крупных бирж скрывается много чего поменьше, и более интересного.
Вот такой список популярных видов проектов я накидал: AMM (автоматические маркет мейкеры), разного рода MEV автоматизация, анти-скам системы, лаунчпады, кредитование, управление активами, разные страховые системы, DEX'ы, DAO, Yield Farming, кроссчейн протоколы, NFT и прочие стартапчики использующие крипту как платежный шлюз.
В следующих частях обязательно рассмотрим каждый компонент DeFi с точки зрения потребителя и разработки в EVM и Solana сетях (может еще и Ton немного затронем, хотя тут пока с реальными DeFi проектами не густо).
Примерный список ключевых тем (на мой скромный взгляд) по DeFi: чем отличаются сети и чейны, обернутые токены, как запускаются токены, стейбл коины, пулл ликвидности, централизованные и децентрализованные платформы, автоматические маркет мейкеры, феномен Uniswap V3 и почему это круто, мемпулл, валидаторы, арбитраж, снайп и прочие боты (+ разные виды атак).
Что из того, что я перечислил выше вам знакомо? Что интересно в первую очередь?
Ну, и как мне кажется этих тем должно быть достаточно для старта DeFi-dev малютки. И, конечно, буду рад если мощные ребята докинут тем и помогут их разобрать.
👍7🔥6🦄2 2
Малюткам DeFi-dev'ам. Часть 2.
С майна первого Биткоина по текущий 2024 год успели родиться стони блокчейнов. Тысячи умерли еще в зачатке. И только десятки дожили до наших дней, и буквально парочка действительно окрепла и расправила крылья над полем децентрализованных финансов.
Сам феномен BTC, как валюты - потрясающий, как и идеи стоящие за блочейном Bitcoin, который по сути был создан ради нее. В отличии от классических финансов, где "хост" валюты - государство у Биткоина хостом служит множество нод, образующих сеть Bitcoin.
Чтобы обеспечить существование самого капитализированного токена без устали работают более 55к узлов сети (на август 2024). Они блок-за-блоком проверяют и передают другим узлам сети данные, которые вносят в них пользователи.
Если вы сталкивались с децентрализованным хранилищем (или хотя бы задавались вопросом), то вы скорее всего слышали о задаче двух генералов и CAP теореме. Если нет, то про первое обязательно прочитайте в Вики, а вот про CAP давайте поговорим.
CAP, где C - Согласованность, А - Доступность, P - устойчивость к разделению. Подумайте или вспомните, что предлагает вам ваша любимая база данных или брокер сообщений. Вспомнили? Как правило, просто достигаются два из трех правил, хотя сейчас с оговорками есть базы удовлетворяющие всем трем. С оговорками потому что достаточно немного поразмыслить, чтобы прийти логически к противоречивости наличия третьего. А теперь вернемся к блокчейн сетям.
Когда проектировалась сеть Биткоина, то она сразу задумывалась как децентрализованная сеть, по которой будут проходить финансовые операции. Потому нет ничего хуже отказа в обслуживании и невозможности обеспечить эту самую децентрализацию (все это с поправкой на реальность). А вот над согласованностью нужно поработать. И результатом работы стал механизм консенсуса PoW (Proof Of Work). Дорогой с точки зрения требуемых мощностей компьютеров и потребляемых энергоресурсов. И как показало время надежный и стабильный.
Еще в годы в университете я разрабатывал клиент для торрента. И вся эта возня с блокченами мне дико напоминала то, как работает этот самый торрент-протокол. Хотя, здесь идеи p2p сетей дошли до совершенно другого уровня.
У нас уже есть сети, объединяющие миллионы компьютеров (интернет). И даже есть те, кто готовы платить за электричество, чтобы хранить наши данные и держать их согласованными. В замен они получают придуманную нами награду. А давайте теперь будем выполнять на них какие-нибудь еще программы. С такими идеями в наш мир ворвался Ethereum.
EVM (Ethereum Virtual Machine) - децентрализованная виртуальная машина, сердце всего Ethereum. Для нее сперва пишется контакт на Solidity, который компилируется (с помощью компилятора solc) в специализированный байт код и выполняется на множестве нод. После компиляции полученную программу нужно также развернуть в сети. Если вам интересно все это пощупать, то возьмите Remix в качестве IDE, Hardhat для тестирования и Truffle для развертывания, ну и обязательно попробуйте развернуть стандартной либой ethers.js. Там все просто берем полученную после компиляции abi'шку (файл с содержащий информацию о внешнем интерфейсе вашей программы), бинарник (файл
С Эфиром работать приятнее всего (в следующих темах постараюсь вас в этом убедить). Пока обращусь к большинству: множество токенов и dapps запущенных на нем - подтверждение моих слов. Комьюнити взрослое, в DeFi понимающее, экосистема самая богатая (но и от нее иногда полыхает мое кресло), так что если решите делать проект - однозначно выбирайте Эфир.
Резюмируя, хоть Биткоин и стал ключевой фигурой в мире, однако, для DeFi это больше историческая фигура, а не что-то, чем сейчас дышит разработка в этой области. Так что его больше мы касаться не будем.
А вот с устройством EVM, нод, токенами на Эфире и вообще всем вокруг EVM-подобных сетей нам предстоит познакомиться далее.
С майна первого Биткоина по текущий 2024 год успели родиться стони блокчейнов. Тысячи умерли еще в зачатке. И только десятки дожили до наших дней, и буквально парочка действительно окрепла и расправила крылья над полем децентрализованных финансов.
Сам феномен BTC, как валюты - потрясающий, как и идеи стоящие за блочейном Bitcoin, который по сути был создан ради нее. В отличии от классических финансов, где "хост" валюты - государство у Биткоина хостом служит множество нод, образующих сеть Bitcoin.
Чтобы обеспечить существование самого капитализированного токена без устали работают более 55к узлов сети (на август 2024). Они блок-за-блоком проверяют и передают другим узлам сети данные, которые вносят в них пользователи.
Если вы сталкивались с децентрализованным хранилищем (или хотя бы задавались вопросом), то вы скорее всего слышали о задаче двух генералов и CAP теореме. Если нет, то про первое обязательно прочитайте в Вики, а вот про CAP давайте поговорим.
CAP, где C - Согласованность, А - Доступность, P - устойчивость к разделению. Подумайте или вспомните, что предлагает вам ваша любимая база данных или брокер сообщений. Вспомнили? Как правило, просто достигаются два из трех правил, хотя сейчас с оговорками есть базы удовлетворяющие всем трем. С оговорками потому что достаточно немного поразмыслить, чтобы прийти логически к противоречивости наличия третьего. А теперь вернемся к блокчейн сетям.
Когда проектировалась сеть Биткоина, то она сразу задумывалась как децентрализованная сеть, по которой будут проходить финансовые операции. Потому нет ничего хуже отказа в обслуживании и невозможности обеспечить эту самую децентрализацию (все это с поправкой на реальность). А вот над согласованностью нужно поработать. И результатом работы стал механизм консенсуса PoW (Proof Of Work). Дорогой с точки зрения требуемых мощностей компьютеров и потребляемых энергоресурсов. И как показало время надежный и стабильный.
Еще в годы в университете я разрабатывал клиент для торрента. И вся эта возня с блокченами мне дико напоминала то, как работает этот самый торрент-протокол. Хотя, здесь идеи p2p сетей дошли до совершенно другого уровня.
У нас уже есть сети, объединяющие миллионы компьютеров (интернет). И даже есть те, кто готовы платить за электричество, чтобы хранить наши данные и держать их согласованными. В замен они получают придуманную нами награду. А давайте теперь будем выполнять на них какие-нибудь еще программы. С такими идеями в наш мир ворвался Ethereum.
EVM (Ethereum Virtual Machine) - децентрализованная виртуальная машина, сердце всего Ethereum. Для нее сперва пишется контакт на Solidity, который компилируется (с помощью компилятора solc) в специализированный байт код и выполняется на множестве нод. После компиляции полученную программу нужно также развернуть в сети. Если вам интересно все это пощупать, то возьмите Remix в качестве IDE, Hardhat для тестирования и Truffle для развертывания, ну и обязательно попробуйте развернуть стандартной либой ethers.js. Там все просто берем полученную после компиляции abi'шку (файл с содержащий информацию о внешнем интерфейсе вашей программы), бинарник (файл
.bin, содержащий вашу програму), рассчитываем нужный газ (берем пока наугад с запасом) и делаем contract.deploy({ data: '0x' + bytecode }).send({}). С Эфиром работать приятнее всего (в следующих темах постараюсь вас в этом убедить). Пока обращусь к большинству: множество токенов и dapps запущенных на нем - подтверждение моих слов. Комьюнити взрослое, в DeFi понимающее, экосистема самая богатая (но и от нее иногда полыхает мое кресло), так что если решите делать проект - однозначно выбирайте Эфир.
Резюмируя, хоть Биткоин и стал ключевой фигурой в мире, однако, для DeFi это больше историческая фигура, а не что-то, чем сейчас дышит разработка в этой области. Так что его больше мы касаться не будем.
А вот с устройством EVM, нод, токенами на Эфире и вообще всем вокруг EVM-подобных сетей нам предстоит познакомиться далее.
Wikipedia
Задача двух генералов
мысленный эксперимент, призванный проиллюстрировать проблему синхронизации состояния двух систем по ненадёжному каналу связи
🔥6❤🔥3 3🦄1
Малюткам DeFi-dev'ам. Часть 3.
И так, у нас есть какой-то байткод для EVM. Этот байткод мы будем выполнять сразу на всех нодах в сети Ethereum, тем самым никакой отдельный узел не может цензурировать наш контракт и исключает мухлеж со стороны держателей конкретной ноды (а уязвимости типа 51% решаются другими механиками).
Вообще каждой ноде не обязательно выполнять контракты, так в Ethereum сети есть три типа нод: полные, облегченные, архивные. Код смарт контрактов выполняют только полные и архивные узлы, а облегченные полагаются на них при валидации транзакций.
Так, а что же значит выполнить транзакцию?
Начнем с отправителя и получателя. Это два адреса. Адрес представляет собой 20-байтовое значения и начинаются с
И так мы - пользователь, отправитель. Наш получатель - контакт
Вот мы зашли на Eatherscan, нашли наш контракт (например, гляньте на контракт Uniswap V2, но далеко не все контракты открытые и есть тулзы для реверса из байткода) и увидели два метода
При вызове
И вот наша транзакция начала распространяться по сети и попадает в mempool (пул неподтвержденных транзакций).
Узлы начинают проверять достаточно ли у отправителя средств для выполнения транзакции, корректность подписи (можете почитать про алгоритм ECDSA) и верно ли указан Gas Limit. Валидаторы (или майнеры в случае с PoW) выбирают транзакции из mempool и включают их в блоки. Каждый блок имеет заголовок, который включает хеш предыдущего блока, временную метку, nonce и другие метаданные.
Когда уже одна из нод получит наше сообщение через вызов provider'а она начнет считать газ: ~700 газа за вызов функции (операция
Стоимость каждой операции в байткоде EVM хорошо задокументирована в спецификации и в нашем простом случае мы смогли легко сами посчитать значение, но так как контакты могут взаимодействовать с другими контрактами не вариант всегда вручную расчитывать цену.
Когда наконец транзакция включена в блок, узлы сети выполняют байткод транзакции на EVM: декодируют данные, вызывается функция, обновляется состояние.
И вот наконец происходит запись изменений в блокчейн, обновляя хранилище контракта. В дальнейшем это состояние будет доступно вызовом функции
И так, у нас есть какой-то байткод для EVM. Этот байткод мы будем выполнять сразу на всех нодах в сети Ethereum, тем самым никакой отдельный узел не может цензурировать наш контракт и исключает мухлеж со стороны держателей конкретной ноды (а уязвимости типа 51% решаются другими механиками).
Вообще каждой ноде не обязательно выполнять контракты, так в Ethereum сети есть три типа нод: полные, облегченные, архивные. Код смарт контрактов выполняют только полные и архивные узлы, а облегченные полагаются на них при валидации транзакций.
Так, а что же значит выполнить транзакцию?
Начнем с отправителя и получателя. Это два адреса. Адрес представляет собой 20-байтовое значения и начинаются с
Ox. Это может быть либо аккаунт пользователя (Externally Owned Accounts aka EOA), либо контракт, адрес которого генерируется при деплое.И так мы - пользователь, отправитель. Наш получатель - контакт
MessageStore который просто сохраняет наши сообщения на блокчейне. И да, помимо выполнения кода контакты могут записывать данные в блокчейн и хранить свое состояние.Вот мы зашли на Eatherscan, нашли наш контракт (например, гляньте на контракт Uniswap V2, но далеко не все контракты открытые и есть тулзы для реверса из байткода) и увидели два метода
setMessage(string memory newMessage и getMessage() public view returns (string memory). Чтож, пора его вызвать. Берем либу ethers тяп ляп и получаем что-то такое:
import { ethers } from "ethers";
const provider = new ethers.providers.InfuraProvider();
const privateKey = '...';
const wallet = new ethers.Wallet(privateKey, provider);
const abi = [
"function setMessage(string memory newMessage) public",
"function getMessage() public view returns (string memory)"
];
const contract = new ethers.Contract(contractAddress, abi, wallet);
const gasPrice = ethers.utils.parseUnits('20', 'gwei');
// Вызываем
const tx = await contract.setMessage("Запись от DeFi малюток!", { gasLimit: 21000, gasPrice });
// Ожидаем подтверждения транзакции
await tx.wait();
При вызове
contract.setMessage мы стартанули транзакцию от отправителя (wallet) в контракт. И вот наша транзакция начала распространяться по сети и попадает в mempool (пул неподтвержденных транзакций).
Узлы начинают проверять достаточно ли у отправителя средств для выполнения транзакции, корректность подписи (можете почитать про алгоритм ECDSA) и верно ли указан Gas Limit. Валидаторы (или майнеры в случае с PoW) выбирают транзакции из mempool и включают их в блоки. Каждый блок имеет заголовок, который включает хеш предыдущего блока, временную метку, nonce и другие метаданные.
Когда уже одна из нод получит наше сообщение через вызов provider'а она начнет считать газ: ~700 газа за вызов функции (операция
CALL),~20к газа за изменение (SSTORE),и 0 за RETURN. Тогда мы легко пройдем наш лимит (мы взяли чутка с излишком) и заплатим за это ~21000 газа * 20 Gwei = 420000 Gwei (0.00042 ETH). Если не указать явно gasLimit either за нас вызовет метод estimateGas - метод выполняет симуляцию транзакции и возвращает приблизительное количество газа, которое потребуется для её выполнения. В зависимости от состояния сети метод может ошибиться и переоценить (юзер заплатит больше) или недооценить стоимость (тогда транзакция упадет с out of gas). За 20 gwei за газ может случиться такое, что никто из валидаторв не возмет нашу транзакцию в обработку (либо ооч долго не будет брать) и если не указать gasPrice - either также может посчитать значение на основе условий сети. Стоимость каждой операции в байткоде EVM хорошо задокументирована в спецификации и в нашем простом случае мы смогли легко сами посчитать значение, но так как контакты могут взаимодействовать с другими контрактами не вариант всегда вручную расчитывать цену.
Когда наконец транзакция включена в блок, узлы сети выполняют байткод транзакции на EVM: декодируют данные, вызывается функция, обновляется состояние.
И вот наконец происходит запись изменений в блокчейн, обновляя хранилище контракта. В дальнейшем это состояние будет доступно вызовом функции
getMessage.Ethereum (ETH) Blockchain Explorer
Uniswap V2: Router 2 | Address: 0x7a250d56...659f2488d | Etherscan
Contract: Verified | Balance: $630,676.85 across 14 Chains | Transactions: 90,546,090 | As at Oct-06-2026 10:15:38 PM (UTC)
🔥2🦄2🌚1
Малюткам DeFi-dev'ам. Часть 4. MEV
И нет, мы не будем мяукать. MEV (aka Maximal Extractable Value) - такая мера прибыли, которую можно получить за счет включения, исключения или переупорядочивания транзакций внутри одного блока. Будем рассматривать на примере Ethereum. Напомню, что блок может включать в себя целый список транзакций и на этапе формирования мы все еще можем добавлять транзакции пока валидаторы (в PoS) не сформируют этот блок и не отправят в чейн (от сюда и БЛОКчейн если че). Тогда все, процесс необратим и включенные блоки иммутабельны.
В MEV'е есть три основные практики:
1. Фронтраннинг. Мы можем легально "подкупить" валидаторов назначив более высокую цену за газ и засунуть нашу транзакцию вперед других.
2. Бэкраннинг. Мы можем отправить транзакцию сразу после ожидаемой трнзакции, чтобы извлечь прыбыль из ее результатов
3. Сендвичинг. Тут мы отправляем две транзакции: одна перед и одна после, чтобы манипулировать ценой промежуточной транзакции.
Звучит как изи мани. Так оно и бывало особенно когда транзакции никак от этого не защищены. И тут надо понять что же нужно для такой атаки.
Перво-наперво нужен быстрый доступ к мемпулу - месту, где транзакции ожидают включения в блок. Это нам нужно, чтобы увидеть потенциальнуб транзакцию-жертву.
Дальше нам нужно научиться определить какую атаку мы хотим совершить. Например, это может быть арбитраж - покупка актива на одной бирже и продажи на другой, ликвидация на платформах кредитования или "фронтран", где мы может "опередить" транзакцию и получить с этого профит.
Понятное дело, что вручную это никто не делает. Для этого ребята пишут MEV ботов которые сразу и мониторят мемпулл, и определяют выгодную транзакцию, и совершают весь этот "атакующий" процесс, ну, и продают токены, приносят прибыль авторам.
Конечно, если бы все так было просто... Ребята из Эфира давно познали дзен и придумали несколько способов защиты от такой атаки (но есть чейны, где такая защита фундаментально невозможна, либо очень сложна и там пытаются вывезти на других механизмах, привет, Solana).
Эфир изменил механизм оплаты комиссии за транзакции (EIP-1559). Разделив комиссию на Base Fee (автоматически регулируется в зависимости от нагрузки на сеть) и Tip Fee (пользователи могут добавить чаевые валидаторам, но это все еще не гарантирует включение в блок). Это усложнило, не исключило возможности для MEV атак (подсчет рисков и прочая математика делает свое дело). Потому еще и появились отдельные проекты защищающие транзакции от MEV атак.
Тут можно выделить несколько проектов такой защиты.
- Flashbot и MEV-Geth - проект создает прозрачный рынок для MEV (не можешь победить - возглавь!). Во первых они устраивают аукцион и создают приватный пул, где майнеры/валидаторы могут выбирать их без риска фронтраннинга.
- Order Matching Protocols - протокол сопоставления ордеров. Также минимизирует ущерб от MEV. RFQ (Request For Quote) - сделки проходят оффчейн и только окончательная транзакция записывается на блокчейн, Batch Auctions - делаем ставки и ваша транзакция одновременно с другими (вместо поочередного выполнения)
- Time-locked Transactions - механизм на некоторых платформах, который откладывает включение транзакции в блок до поры-до времени.
- Privacy Enhancements - обеспечение приватности транзакций, скрывая их детали до включения блок. Тут популярны два протокола - zk-SNARKs и zk-Rollups.
- Decentralized Sequencers - похоже на RFQ, только децентрализованно (использую тн sequencers). Тут популярны проект Arbitrum и Optimism.
- Commit-Reveal Schemes - тут система такая: пользователь сперва отправляет commit (хеш транзакции), а затем раскрывает ее детали. Это предотвращает фронтраннинг, так как детали транзакции известны только после коммита.
В целом, все это не убило полностью, но сильно усложнило MEV-атаки. Вы всегда можете попробовать поискать другие уязвимости и более изощренные схемы. А я думаю, что пора перейти к расмотрению других блокчейнов.
И нет, мы не будем мяукать. MEV (aka Maximal Extractable Value) - такая мера прибыли, которую можно получить за счет включения, исключения или переупорядочивания транзакций внутри одного блока. Будем рассматривать на примере Ethereum. Напомню, что блок может включать в себя целый список транзакций и на этапе формирования мы все еще можем добавлять транзакции пока валидаторы (в PoS) не сформируют этот блок и не отправят в чейн (от сюда и БЛОКчейн если че). Тогда все, процесс необратим и включенные блоки иммутабельны.
В MEV'е есть три основные практики:
1. Фронтраннинг. Мы можем легально "подкупить" валидаторов назначив более высокую цену за газ и засунуть нашу транзакцию вперед других.
2. Бэкраннинг. Мы можем отправить транзакцию сразу после ожидаемой трнзакции, чтобы извлечь прыбыль из ее результатов
3. Сендвичинг. Тут мы отправляем две транзакции: одна перед и одна после, чтобы манипулировать ценой промежуточной транзакции.
Звучит как изи мани. Так оно и бывало особенно когда транзакции никак от этого не защищены. И тут надо понять что же нужно для такой атаки.
Перво-наперво нужен быстрый доступ к мемпулу - месту, где транзакции ожидают включения в блок. Это нам нужно, чтобы увидеть потенциальнуб транзакцию-жертву.
Дальше нам нужно научиться определить какую атаку мы хотим совершить. Например, это может быть арбитраж - покупка актива на одной бирже и продажи на другой, ликвидация на платформах кредитования или "фронтран", где мы может "опередить" транзакцию и получить с этого профит.
Понятное дело, что вручную это никто не делает. Для этого ребята пишут MEV ботов которые сразу и мониторят мемпулл, и определяют выгодную транзакцию, и совершают весь этот "атакующий" процесс, ну, и продают токены, приносят прибыль авторам.
Конечно, если бы все так было просто... Ребята из Эфира давно познали дзен и придумали несколько способов защиты от такой атаки (но есть чейны, где такая защита фундаментально невозможна, либо очень сложна и там пытаются вывезти на других механизмах, привет, Solana).
Эфир изменил механизм оплаты комиссии за транзакции (EIP-1559). Разделив комиссию на Base Fee (автоматически регулируется в зависимости от нагрузки на сеть) и Tip Fee (пользователи могут добавить чаевые валидаторам, но это все еще не гарантирует включение в блок). Это усложнило, не исключило возможности для MEV атак (подсчет рисков и прочая математика делает свое дело). Потому еще и появились отдельные проекты защищающие транзакции от MEV атак.
Тут можно выделить несколько проектов такой защиты.
- Flashbot и MEV-Geth - проект создает прозрачный рынок для MEV (не можешь победить - возглавь!). Во первых они устраивают аукцион и создают приватный пул, где майнеры/валидаторы могут выбирать их без риска фронтраннинга.
- Order Matching Protocols - протокол сопоставления ордеров. Также минимизирует ущерб от MEV. RFQ (Request For Quote) - сделки проходят оффчейн и только окончательная транзакция записывается на блокчейн, Batch Auctions - делаем ставки и ваша транзакция одновременно с другими (вместо поочередного выполнения)
- Time-locked Transactions - механизм на некоторых платформах, который откладывает включение транзакции в блок до поры-до времени.
- Privacy Enhancements - обеспечение приватности транзакций, скрывая их детали до включения блок. Тут популярны два протокола - zk-SNARKs и zk-Rollups.
- Decentralized Sequencers - похоже на RFQ, только децентрализованно (использую тн sequencers). Тут популярны проект Arbitrum и Optimism.
- Commit-Reveal Schemes - тут система такая: пользователь сперва отправляет commit (хеш транзакции), а затем раскрывает ее детали. Это предотвращает фронтраннинг, так как детали транзакции известны только после коммита.
В целом, все это не убило полностью, но сильно усложнило MEV-атаки. Вы всегда можете попробовать поискать другие уязвимости и более изощренные схемы. А я думаю, что пора перейти к расмотрению других блокчейнов.
Flashbots
A research and development organization mitigating the negative externalities posed by MEV.
1🔥3🦄2