Вивчення основних механізмів UniswapV4

РозширенийDec 24, 2023
У цій статті розглядаються три інноваційні функції UniswapV4 — Flash Accounting, Singleton Contract і Hooks Architecture — з точки зору коду та реалізації.
Вивчення основних механізмів UniswapV4

Вступ:

З моменту анонсу UniswapV4 ця платформа обміну зазнала значних змін, перетворившись із простої платформи обміну на постачальника інфраструктурних послуг. Зокрема, функція Hooks у V4 привернула широку увагу. Після поглибленого дослідження я зібрав певний вміст, щоб допомогти всім краще зрозуміти цю трансформацію та її впровадження.

Інновації UniswapV4 спрямовані не лише на вдосконалення технології AMM, а й на розширення екосистеми. Зокрема, це нововведення включає такі ключові функції:

  • Флеш-облік
  • Одиночний контракт
  • Архітектура гаків

У наступних розділах я детально поясню значення цих функцій і принципи їх реалізації.

Джерело: https://twitter.com/jermywkh/status/1670779830621851650

Флеш-облік

Подвійна бухгалтерія

UniswapV4 використовує метод ведення записів, схожий на подвійну бухгалтерію, щоб відстежувати зміни балансу токенів, що відповідають кожній операції. Цей метод подвійної бухгалтерії вимагає запису кожної транзакції на кількох рахунках одночасно та забезпечення збалансованості балансу активів між цими рахунками. Наприклад, припустимо, що користувач обмінює 100 TokenA на 50 TokenB із пулу. Запис у книзі буде таким:

  • КОРИСТУВАЧ: TokenA зменшився на 100 одиниць (-100), а TokenB збільшився на 50 одиниць (+50).
  • POOL: TokenA збільшився на 100 одиниць (+100), тоді як TokenB зменшився на 50 одиниць (-50).

Токен Дельта та пов’язані операції

У UniswapV4 цей метод ведення записів в основному використовується для основних операцій і змінної зберігання під назвою lockState.currencyDelta[валюта] використовується в коді для запису суми змін балансу токенів. Якщо значення цієї дельти додатне, це означає очікуване збільшення кількості токенів у пулі, тоді як від’ємне значення означає очікуване зменшення кількості токенів. Крім того, якщо значення додатне, воно вказує на кількість нестачі токенів у пулі (очікувана сума, яку потрібно отримати), тоді як від’ємне значення вказує на надлишок токенів у пулі (очікувана сума, яку користувачі можуть вилучити). У наведеному нижче списку показано вплив різних операцій на токен Delta:

  • modifyPosition: представляє операцію Add/Remove ліквідності. Для додавання ліквідності TokenDelta оновлюється за допомогою додавання (представляє очікувану кількість TokenA, яка буде додана до пулу). Для видалення ліквідності TokenDelta оновлюється за допомогою віднімання (представляє очікувану кількість TokenB, яку потрібно вилучити з пулу).
  • swap: представляє операцію Swap. Беручи приклад заміни TokenA на TokenB, TokenADelta оновлюється за допомогою додавання, тоді як TokenBDelta оновлюється за допомогою віднімання.
  • settle: супроводжує передачу токенів до пулу. Пул обчислює збільшення кількості токенів до і після та оновлює TokenDelta за допомогою віднімання. Якщо пул отримує очікувану кількість токенів, віднімання обнулить TokenDelta.
  • взяти: супроводжує виведення токенів із пулу. TokenDelta оновлюється за допомогою додавання, що вказує на те, що маркери видалено з пулу.
  • Монетний двір: Поведінка оновлення TokenDelta схожа на «взяти», але замість того, щоб фактично вилучати токени з пулу, токени ERC1155 видаються як доказ вилучення, тоді як токени залишаються в пулі. Пізніше користувачі зможуть отримати токени з пулу, записавши токени ERC1155. Мета цього підходу може бути подвійною: 1. Економія витрат на газ для передачі токенів ERC20 (виклик за контрактом + один запис у пам’ять менше) і використання запису токенів ERC1155 у майбутньому для оновлення TokenDelta для транзакцій. 2. Зберігайте ліквідність у пулі, щоб підтримувати глибокий пул ліквідності для кращого досвіду обміну користувачами.
  • donate: ця операція оголошує пожертвування токенів пулу, але насправді токени все одно потрібно передавати до пулу за допомогою «settle». Тому в цьому випадку TokenDelta оновлюється за допомогою додавання.

Серед цих операцій лише «settle» і «take» передбачають фактичну передачу токенів, тоді як інші операції відповідають виключно за оновлення значення TokenDelta.

Приклад токена Дельта

Тут ми використовуємо простий приклад, щоб проілюструвати, як оновити TokenDelta. Припустімо, що сьогодні ми обмінюємо 100 TokenA на 50 TokenB:

  1. Перед початком транзакції і TokenADelta, і TokenBDelta дорівнюють 0.
  2. swap: обчисліть, скільки TokenA має отримати пул і скільки TokenB отримає користувач. На даний момент TokenADelta = 100, TokenBDelta = -50.
  3. розрахунки: надішліть 100 TokenA до Пулу та оновіть TokenADelta = 100 - 100 = 0.
  4. взяти: передайте 50 TokenB з пулу на обліковий запис користувача та оновіть TokenBDelta = -50 + 50 = 0.
  5. Після завершення транзакції і TokenADelta, і TokenBDelta дорівнюють 0.

Коли вся операція обміну завершена, і TokenADelta, і TokenBDelta скидаються до 0. Це означає, що операцію було повністю збалансовано, таким чином забезпечуючи послідовність залишків на рахунку.

EIP-1153: тимчасові коди операцій зберігання

Раніше згадувалося, що UniswapV4 використовує змінні зберігання для запису TokenDelta. Однак у рамках контракту читання та запис до змінних зберігання є досить дорогими. Це підводить нас до іншого EIP, представленого Uniswap: EIP1153 - Transient Storage Opcodes.

UniswapV4 планує використовувати коди операцій TSTORE і TLOAD, надані EIP1153 для оновлення TokenDelta. Змінні пам’яті, які використовують коди операцій Transient Storage, будуть видалені після завершення транзакції (подібно до змінних пам’яті), таким чином зменшуючи плату за газ.

Було підтверджено, що EIP1153 буде включено до майбутнього оновлення в Канкуні, а UniswapV4 також заявив, що він запрацює після оновлення в Канкуні, як повідомляється тут.

джерело: https://etherworld.co/2022/12/13/transient-storage-for-beginners/

Flash Accounting — Lock

UniswapV4 представляє механізм блокування, який означає, що перед виконанням будь-якої операції пулу ви повинні спочатку викликати PoolManager.lock(), щоб отримати блокування. Під час виконання lock() він перевіряє, чи значення TokenDelta дорівнює 0, інакше він повернеться. Після успішного отримання PoolManager.lock() він викликає функцію lockAcquired() msg.sender. Усередині функції lockAcquired() виконуються операції, пов’язані з пулом, такі як swap і modifyPosition.

Процес показано нижче. Коли користувачеві потрібно виконати операцію Token Swap, він повинен викликати смарт-контракт за допомогою функції lockAcquired() (іменованої контрактом зворотного виклику). Контракт зворотного виклику спочатку викликає PoolManager.lock(), а потім PoolManager викликає функцію lockAcquired() контракту зворотного виклику. Усередині функції lockAcquired() визначається логіка, пов’язана з операціями пулу, такими як своп, сеттл і тейк. Нарешті, коли lock() збирається закінчитися, PoolManager перевіряє, чи TokenDelta, пов’язаний із цією операцією, було скинуто до 0, гарантуючи, що баланс активів у пулі залишається незмінним.

Одиночний контракт

Singleton Contract означає, що UniswapV4 відмовився від попередньої моделі Factory-Pool. Кожен пул більше не є незалежним смарт-контрактом, але всі пули мають спільний єдиний контракт. Цей дизайн у поєднанні з механізмом Flash Accounting вимагає лише оновлення необхідних змінних зберігання, що ще більше зменшує складність і вартість операцій.

У наведеному нижче прикладі, використовуючи як приклад UniswapV3, для обміну ETH на DAI знадобиться принаймні чотири передачі токенів (операції запису в сховище). Це включає численні зміни, зареєстровані для токенів USDC, USDT і DAI. Однак завдяки вдосконаленням UniswapV4 у поєднанні з механізмом Flash Accounting потрібна лише одна передача маркера (переміщення DAI з пулу до користувача), що значно зменшує кількість операцій і витрати.

Джерело: https://twitter.com/Uniswap/status/1671208668304486404

Архітектура гаків

В останньому оновленні UniswapV4 найбільш помітною функцією є архітектура хуків. Це оновлення надає велику гнучкість щодо доступності пулу. Хуки — це додаткові дії, які запускаються через Контракт хуків під час виконання певних операцій у Пулі. Ці дії поділяються на ініціалізацію (створення пулу), modifyPosition (додавання/видалення ліквідності), обмін і пожертвування. Кожна категорія має дії до та після виконання.

  • beforeInitialize / afterInitialize
  • beforeModifyPosition / afterModifyPosition
  • beforeSwap / afterSwap
  • beforeDonate / afterDonate

Ця конструкція дозволяє користувачам виконувати спеціальну логіку до та після певних операцій, що робить її більш гнучкою та розширює функціональність UniswapV4.

Джерело: https://github.com/Uniswap/v4-core/blob/main/whitepaper-v4-draft.pdf

Приклад хука — хук лімітного замовлення

Далі ми використаємо приклад лімітного замовлення, щоб пояснити фактичний процес роботи хуків у UniswapV4. Перш ніж почати, давайте коротко пояснимо принцип реалізації лімітних ордерів у UniswapV4.

Механізм лімітного замовлення UniswapV4

Реалізація лімітного замовлення UniswapV4 працює шляхом додавання ліквідності до певного цінового діапазону, а потім виконання операції видалення ліквідності, якщо ліквідність у цьому діапазоні обмінюється.

Наприклад, скажімо, ми додаємо ліквідність у ціновому діапазоні 1900-2000 для ETH, а потім ціна ETH піднімається з 1800 до 2100. На даний момент всю ліквідність ETH, яку ми раніше додали в ціновому діапазоні 1900-2000, було обміняно на USDC (припускаємо, що в пулі ETH-USDC). Усунувши ліквідність у цей момент, ми можемо досягти ефекту, подібного до виконання ринкового ордера ETH за поточним ціновим діапазоном 1900-2000.

Лімітний ордер

Цей приклад взято з GitHub UniswapV4. У цьому прикладі контракт Limit Order Hook надає два хуки, а саме afterInitialize і afterSwap. Хук afterInitialize використовується для запису цінового діапазону (тіку) під час створення пулу, щоб визначити, які лімітні ордери були зіставлені після обміну.

Виставити ордер

Коли користувачеві потрібно розмістити замовлення, контракт Hook виконує операцію додавання ліквідності на основі вказаного користувачем цінового діапазону та кількості. У контракті Hook для лімітних замовлень ви можете побачити функцію place() . Основна логіка полягає в тому, щоб викликати функцію lockAcquiredPlace() після отримання блокування для виконання операції додавання ліквідності, що еквівалентно розміщенню лімітного замовлення.

джерело: https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol#L246

хук afterSwap

Після того як користувач заповнить токен Swap у цьому пулі, пул викличе функцію afterSwap() контракту Hook. Основна логіка afterSwap полягає у вилученні ліквідності раніше розміщених ордерів, які були виконані між попереднім ціновим діапазоном і поточним ціновим діапазоном. Така поведінка еквівалентна виконанню замовлення.

джерело: https://github.com/Uniswap/v4-periphery/blob/main/contracts/hooks/examples/LimitOrder.sol#L192

Потік обмежених ордерів

Ось блок-схема, яка ілюструє процес виконання лімітного замовлення:

  1. Розміщувач замовлення надсилає замовлення до контракту Hook.
  2. Контракт Hook виконує операції додавання ліквідності на основі інформації про замовлення.
  3. Звичайні користувачі виконують операції Token Swap у Пулі.
  4. Після завершення операції Swap Token пул викликає функцію afterSwap() контракту Hook.
  5. Контракт Hook виконує операції з видалення ліквідності для виконаних лімітних ордерів на основі зміни цінового діапазону токенів, що обмінюються.

Вище наведено повний процес впровадження лімітного ордера за допомогою механізму Hook.

Гачок: інші особливості

Хуки мають кілька цікавих моментів, якими, на мій погляд, варто поділитися.

Біт адреси контракту хуків

Рішення про виконання конкретних операцій до/після визначається крайнім лівим 1 байтом адреси контракту Hook. 1 байт дорівнює 8 бітам, що відповідає 8 додатковим діям. Пул перевірить, чи біт цієї дії дорівнює 1, щоб визначити, чи потрібно викликати відповідну функцію підключення в контракті підключення. Це також означає, що адреса Hook-контракту має бути розроблена певним чином і не може бути довільно обрана як Hook-контракт. Ця конструкція в основному спрямована на зменшення споживання газу та перенесення витрат на розгортання за контрактом для досягнення більш ефективної роботи. (PS: на практиці різні солі CREATE2 можна використовувати для грубого обчислення адрес контрактів, які відповідають умовам)

Динамічна плата

Окрім можливості виконувати додаткові операції до та після кожної дії, хуки також підтримують впровадження динамічних комісій. Під час створення пулу ви можете вказати, чи вмикати динамічні комісії. Якщо ввімкнено динамічні комісії, функція getFee() контракту Hook викликається під час обміну токенів. Контракт Hook може визначати розмір комісії, що стягується на основі поточного стану пулу. Така конструкція дозволяє здійснювати гнучкий розрахунок комісії на основі фактичних обставин, підвищуючи гнучкість системи.

Створення басейну

Кожен пул повинен визначити контракт Hook під час створення, і його не можна змінити згодом (хоча різні пули можуть спільно використовувати той самий контракт Hook). Головним чином це відбувається тому, що хуки вважаються частиною PoolKey, а PoolManager використовує PoolKey, щоб визначити, з яким пулом працювати. Навіть якщо активи однакові, якщо контракт Hook відрізняється, він вважатиметься іншим пулом. Ця конструкція гарантує, що станом і операціями різних пулів можна керувати незалежно, забезпечуючи узгодженість пулів. Однак це також збільшує складність маршрутизації, оскільки збільшується кількість пулів (можливо, UniswapX призначений для вирішення цієї проблеми).

TL; DR

  • Flash Accounting використовується для відстеження змін кількості кожного токена, щоб переконатися, що всі зміни обнулені після завершення транзакції. Щоб заощадити на оплаті газу, Flash Accounting використовує спеціальний метод зберігання, передбачений EIP1153.
  • Дизайн Singleton Contract допомагає зменшити споживання газу, уникаючи оновлення кількох змінних зберігання.
  • Архітектура хуків передбачає додаткові операції, розділені на етапи перед виконанням і після виконання. Це забезпечує більшу гнучкість у кожній операції пулу, але також ускладнює маршрутизацію пулів.

UniswapV4 чітко наголошує на розширенні всієї екосистеми Uniswap, перетворюючи її на інфраструктуру, яка дозволить створювати більше послуг на основі пулів Uniswap. Це сприяє підвищенню конкурентоспроможності Uniswap і знижує ризик альтернативних послуг. Однак чи досягне він очікуваного успіху, ще невідомо. Деякі основні моменти включають поєднання Flash Accounting і EIP1153, і ми вважаємо, що в майбутньому ці функції будуть використовувати більше служб, що призведе до різних сценаріїв застосування. Це основна концепція UniswapV4, і ми сподіваємося, що вона дає змогу глибше зрозуміти, як працює UniswapV4. Якщо в статті є якісь помилки, будь ласка, вказуйте на них. Ми також раді обговоренням і відгукам.

Нарешті, ми хотіли б подякувати Антону Ченгу та Пін Чену за перегляд статті та надання цінних відгуків!

Відмова від відповідальності:

  1. Цю статтю передруковано з [середовище]. Усі авторські права належать оригінальному автору [林瑋宸 Albert Lin]. Якщо є заперечення щодо цього передруку, зв’яжіться з командою Gate Learn(gatelearn@gate.io), і вони негайно вирішать це.
  2. Відмова від відповідальності: погляди та думки, висловлені в цій статті, належать виключно автору та не є жодною інвестиційною порадою.
  3. Переклади статті на інші мови виконує команда Gate Learn. Якщо не зазначено вище, копіювання, розповсюдження або плагіат перекладених статей заборонено.
* Ця інформація не є фінансовою порадою чи будь-якою іншою рекомендацією, запропонованою чи схваленою Gate.io.
* Цю статтю заборонено відтворювати, передавати чи копіювати без посилання на Gate.io. Порушення є порушенням Закону про авторське право і може бути предметом судового розгляду.
Розпочати зараз
Зареєструйтеся та отримайте ваучер на
$100
!
Створити обліковий запис