Думаю, разбор Uniswap контрактов нужно было начинать именно с этого поста, но как-то дошел до не в самом конце. Поэтому в общей сводке всех постов по теме, поставлю его в самом начале.
Архитектура Uniswap V2 удивительно проста.
В ее основе лежит контракт UniswapV2Pair, в котором хранятся два токена ERC20. Через него трейдеры могут менять токены один на другой, а поставщики ликвидности - предоставлять эти самые токены.
Для каждой возможной пары UniswapV2Pair существует свой одноименный контракт, который управляет ею. Если нужный контракт UniswapV2Pair не существует, новый может быть создан из контракта UniswapV2Factory.
Контракты UniswapV2Pair также являются токенами ERC20 (и наследуют от ERC20). Этот токен используется для отслеживания депозитов аналогично тому, как работает ERC4626.
Хотя продвинутые трейдеры или смарт-контракты могут напрямую взаимодействовать с контрактом пары, большинство пользователей будут взаимодействовать с токенами через контракт Router, который имеет несколько удобных функций, например, торговля между парами в одной транзакции и создание «синтетической» пары, если таковой не существует.
Паттерн "core - periphery"
Обратите внимание, что контракт маршрутизатора находится в репозитории под названием «v2 periphery», а пара - в репозитории «v2 core».
Uniswap V2 следует шаблону проектирования «ядро / периферия», где наиболее важная логика находится в ядре, а «необязательная» логика - на периферии.
Это делается для того, чтобы основные контракты содержали как можно меньше кода, что снижает вероятность возникновения ошибок в основной бизнес-логике.
Как определить адрес пула через токены?
Вместо того чтобы обращаться к маппингу пар токенов с адресом пула, смарт-контракты вычисляют адрес пула, предсказывая адрес через create2, как функцию от адресов токенов и адреса фабрики.
Поскольку при этом не происходит обращения к хранилищу, это очень экономит газ. Ниже приведена вспомогательная функция из UniswapV2Library для вычисления адреса парного контракта:
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
))));
}Почему не использовать клоны контрактов через EIP1167?
Многие знают, что иногда копии контрактов создаются с помощью стандарта EIP1167, так почему же Uniswap не использует данную логику?
На самом деле все также просто.
Не смотря на то, что через EIP1167 развертывание контракта копии будет дешевле, но приводит к дополнительным тратам 2300 газа за транзакцию, так как используется delegatecall. И поскольку пулы предназначены для частого использования, экономия от развертывания будет потеряна после нескольких сотен транзакций, поэтому по итогу проще создавать новый контракт с нуля.
Надеюсь, вам понравились наши небольшие разборы Uniswap V2. На этой неделе выйдет еще пара постов про AMM, а потом перейдем к AAVE и Compound.
#uniswap #core #periphery