D2
Администратор
- Регистрация
- 19 Фев 2025
- Сообщения
- 4,380
- Реакции
- 0
Привет! Давно не виделись.
Новых идей и интересностей у меня нет, поэтому получайте комком информацию по смарт-контрактам, дрейнерам, атакам, смотря кому что интересно.
Про Etherium, смарт-контракты и т.д.Новых идей и интересностей у меня нет, поэтому получайте комком информацию по смарт-контрактам, дрейнерам, атакам, смотря кому что интересно.
Сейчас будет скучно, душно, тягуче. Но тот кому это надо, прочитает до конца уверен.
Если же вы пользуетесь в своей жизни поговоркой 'зачем мне понимать что это, если оно итак работает', то вы можете спокойно листать ниже , тут вы не найдёте ничего интересного. Абсолютно.
Всем остальным:
Итак все знают, что Ethereum - это блокчейн платформа,децентрализованная платформа, с открытым исходным кодом, для создания DAPP'ов и cмартконтрактов, надо знать хотя бы это. В нем используется P2P(одноранговая, об этом чуть ниже) сеть узлов.
Ethereum - сеть (часто называемый "эфиром" или "eth") появлялся перед «эфиром» - криптовалютой.
Ethereum по некоторым предположениям была предложена Виталиком Бутерином в конце 2013 года, и работа по развитию началась в начале 2014 года.
Основная, главная, нативная валюта Ethereum, "эфир" (в простонародье «ETH»), используется в Ethereum для всего, например, оплата fee за транзакции и взаимодействие с смартконтрактами.
Эфир(eth) был создан как часть первоначальной архитектуры, когда сеть Ethereum начала жить.
Попрошу заметить, что Виталик и его команда приводят в жизнь идеи придуманные до них.
Корни блокчейн платформы по типу нынешнего эфира можно проследить до 1980 -х и 1990 -х годов, тогда Дэвид Чаум(вики содержит инфу о нём) изучал криптографические протоколы для цифровых денег.
Такой персонаж как Ник Сзабо создал концепцию смартконтрактов в конце 1990-х годов, самовоспроизводимые куски кода с записанными в них условиями/правилами/соглашениями. В 2005 году он создал Bit Gold, можно почитать в гугле как он к этому пришел, достаточно интересная история.
Ethereum был в основном разработан с использованием языка go(Golang, Гошка).
Го ведь можно считать низкоуровневым компилируемым?
Клиент Go Ethereum, так же известный нам как "Geth", является одной из популярных реализаций протокола Ethereum, и он написан на го.
Geth позволяет участвовать в сети Ethereum, проверять транзакции и запускать смартконтракты.
Хочу так же упомянуть, что есть другие клиенты, на других языках, Python (Pyethereum), Rust (Parity) и т.д.
В дополнение к вышеперечисленным, люди обычно используют языки более легкие, такие как Solidity для смартконтрактов, которые работают на EVM.
Solidity, с синтаксисом как у JavaScript, является наиболее часто используемым языком. Другие языки, Vyper и LLL, используются(вроде как), но я не пробовал.
К питупи (p2p).
Одноранговая сеть узлов, представляет собой архитектуру, в которой отдельные устройства - "узлы" связываются напрямую друг с другом без центрального сервера. Все узлы в сети равны, и ни один узел не имеет привелегий больше чем другой.
Если интересно почему p2p - круто, гуглите.
Узлы могут напрямую общаться, обмениваться данными, без посредников. Сети демонстрируют высокую избыточность, сеть может продолжать работу даже в случае сбоя одного канала или порта так как несколько узлов могут хранить и реплицировать данные. P2P по своей природе масштабируются. Поэтому П2П используется в IPFS и эфире, проще говоря - круче придумать сложно.
Gas gas gas i'm gonna step on the gas.
В Ethereum вычисления и взаимодействия с блокчейном что-то потребляют, вычислительные ресурсы которые назвали "газ".
В PoW платили газ, как поощрение майнерам за обработку своих транзакций. Чем сложнее операция или контракт, тем больше газа требуется.
В PoS вместо того, чтобы решать головоломки, валидаторы (стейкеры/валидаторы) выбираются для создания новых блоков и проверки транзакций на основе количества криптовалюты, которую они закладывают как депозит на обеспечение работы сети.
Почему газ, а не уголь или керосин?
Газ это если что не реальный отсчёт газа, а аллегорическое название от английского Gas как топливо для машины, чтобы двигатель работал ему нужнен Gas, собственно поэтому было выдано название газ, ведь на нём едет сеть эфира.
С этими стандартными знаниями можно переходить к делам насущным.
Выливаю на вас ведром информацию про хорошие функции в смарт контрактах которые вы можете использовать.
Юзайте Pulse chain в связке с Base chain. Это хардфорк эфира, модель дефляционная, более быстрое время блока (10 секунд вместо 12), более низкие платы за газ (на основе рыночного спроса вместо фиксированных пределов) и сжигание комиссий (25% сборов уничтожаются). Must have для вашего дрейнера, например.
Пишите свои контракты с нуля и давайте им кастомные имена. РАНДОМИЗИРУЙТЕ. Не страшно взять чужой код с гита и переписать под себя, зато так вы избежите большинства проблем.
Чтобы дать возможность кастомно давать имена, вы можете использовать строковую переменную для хранения имени функции, а затем использовать функцию Bytes4 для преобразования строки в подпись функции.
Например:
JavaScript: Скопировать в буфер обмена
Код:
pragma solidity ^0.8.0;
contract castomnoNaming{
string public functionName;
constructor(string memory _functionName) {
functionName = _functionName;
}
function callFunction() public view returns (string memory) {
bytes4 signature = bytes4(keccak256(bytes(functionName)));
return string(abi.encodePacked(signature));
}
}
Функция callFunction использует хэш-функцию Keccak256 для создания уникальной 4-байтовой подписи для имени функции. Функция abi.encodepack затем используется для преобразования сигнатуры в строку, которая может быть возвращена функцией.
Можно настроить переменную FunctionName с любым значением строки, которое вы хотите, и функция callFunction будет генерировать уникальную сигнатуру для нее.
В контексте солидити сигнатура(signature/подпись) является уникальным идентификатором для функции, которая генерируется путем хэширования имени функции и типов входных параметров.
Функция Bytes4 используется для преобразования хэш в 4-байтовую подпись, которую можно использовать для вызова функций.
В примере кода выше функция callFunction использует хеш-функцию Keccak256 для генерации уникальной 4-байтовой подписи для переменной FunctionName.
Функция abi.encodepack затем используется для преобразования подписи в строку, которая может быть возвращена функцией снова.
Пермиты. Permit и Permit2.
Permit позволяет пользователю дать разрешение контракту на использование своих токенов в одной транзакции, без необходимости выполнения approve(иногда нужно). Это упрощает работу, повышает безопасность и снижает трату газа.
Permit введён в стандарте EIP-2612, сам стандарт вводит новую функцию permit() в стандарт ERC-20.
Функция permit() принимает набор параметров, таких как владелец, расходник, сумма, срок и подпись владельца.
Подпись формируется путем подписания фрагмента данных, включающего имя токена, версию, идентификатор цепочки и nonce. Nonce - это уникальное число, которое предотвращает атаки повторного воспроизведения.
Чтобы воспользоваться пермитом, юзер должен подписать данные своим закрытым ключом и отправить их к вашему контракту, который пытается юзануть токены юзера.
Затем ваш контракт может вызвать функцию permit() на контракте токена(ов), указав в качестве аргумента подписанные данные. Это приведет к обновлению баланса потенциального mammonth до указанной суммы.
Затем ваш контракт может перевести токены со счета владельца с помощью функции transferFrom().
Но почему все контракты юзают пермит2?
Разница между пермит и permit2 заключается в том, что permit - это функция, которая работает только для токенов, поддерживающих стандарт EIP-2612, а permit2 - это функция, которая работает для любого токена ERC-20, даже если он не поддерживает EIP-2612.
Популярные кейсы для Permit2 - это оплата транзакций токенами(те самые fees), мета-транзакции, флэш-кредиты и децентрализованные биржи(DEX).
Как permit, так и permit2 позволяют пользователям утверждать передачу токенов с помощью подписанного сообщения вместо отправки транзакции.
Это позволяет сэкономить на газе.
Однако permit2 также требует дополнительного параметра target, который представляет собой адрес контракта, выполняющего перевод токенов
Пример пермита основанный на EIP-2612
JavaScript: Скопировать в буфер обмена
Код:
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, "ERC20Permit: expired deadline");
bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), _getChainId(), address(this)));
bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline));
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "ERC20Permit: invalid signature");
_approve(owner, spender, value);
}
Пример пермита2 основанный на Permit2
JavaScript: Скопировать в буфер обмена
Код:
function permit2(address owner, address target, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, "ERC20Permit: expired deadline");
bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), _getChainId(), address(this)));
bytes32 structHash = keccak256(abi.encode(PERMIT2_TYPEHASH, owner, target, tokenId));
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "ERC20Permit: invalid signature");
_transfer(owner, target, tokenId);
}
Основные отличия заключаются в следующем:
permit использует value для указания количества токенов, которое необходимо одобрить для расходующего, а permit2 использует tokenId для идентификации конкретного токена, который необходимо передать целевому.
permit вызывает _approve, чтобы обновить количество токенов у расходующего, а permit2 вызывает _transfer, чтобы переместить токен от владельца к получателю.
permit использует nonce, основанный на адресе владельца, для предотвращения атак повторного воспроизведения, в то время как permit2 не нуждается в nonce, поскольку каждый токен имеет уникальный идентификатор.
Approve
Апрув - это термин, используемый для описания процесса предоставления разрешения другому контракту или адресу на расходование определенного количества токенов от имени владельца.
Это делается путем вызова функции approve из контракта ERC20/ERC721, которая принимает 2 аргумента - адрес расходующего и количество токенов.
Функция approve выдает событие Approval и обновляет карту allowance mapping. В маппинг летит лог сколько каждый тратящий может потратить из баланса каждого владельца.
SAFA (Single Approval For All) - простой и удобный способ подтверждения контракта для расходования любого количества токенов с баланса владельца (в ERC20 не существует встроенной фукнции SAFA, поэтому для 20-ки используем Permit2, для 721 же есть SetApprovalForAll внутри контракта, причем следует упомянуть, что мы можем вызывать её оффчейном, с помощью джаваскрипта и библиотеки Web3.JS).
Для этого вызывается функция approve с максимально возможным значением (2^256 - 1) в качестве аргумента amount. Таким образом, владельцу не нужно вызывать функцию approve повторно для последующих транзакций с тем же самым тратящим.
На деле является одной из самых вкусных функций на равне с batchPermit.
Single Approve(сингл) - более безопасный способ одобрения на расходование токенов.
Для этого нужно вызвать функцию approve, указав в качестве аргумента amount точное количество токенов. Таким образом, мы ограничиваем токены для тратящего и предотвращаем возможные фроды.
Однако это также требует больших затрат на газ, вызывать функцию approve каждый раз, когда надо взаимодействовать с одним и тем же отправителем - затратно. Поэтому часто об разрабы контрактов просто забывают об этой мелочи, при желании можете попробовать деплоить контракт с синглом без проверок, дабы качать бабки из какого-нибудь noname контракта.
ERC20 Approve - стандартный способ одобрения расходование определенного количества токенов. Для этого вызывается функция approve с неким положительным количеством токенов в качестве аргумента amount. Функция approve проверяет, существует ли уже надбавка для того же пользователя, и если да, то заменяет баланс на новую сумму.
Таким образом, владелец может в любой момент обновить или отозвать свое разрешение, вызвав функцию approve еще раз с другой суммой.
При этом возникает уязвимость, известная как multiple withdrawal. Если интересно, то изучай тут.
Auto-Claim Old Approval (ACOA)
Функция, позволяющая получать токены без необходимости каждый раз подтверждать перевод.
Работает так: смартконтракт устанавливает высокий лимит на расходование токенов пользователя, а затем с помощью функции _transfer() отправляет токены на адрес пользователя.
Таким образом, можно требовать токены многократно, не оплачивая газ.
Глядите на контракт и замечайте вот такую фишку
JavaScript: Скопировать в буфер обмена
Код:
function autoClaimOldApproval(address receiver, uint256 amount) public returns (bool) {
require(allowance(receiver, address(this)) >= amount, "Insufficient allowance");
_transfer(address(this), receiver, amount);
return true;
}
}
Если в коде нет строчки require, это значит что функция не проверяет то есть ли у пользователя позволение на выполнение действий с контрактом, таким образом это по сути самый легкий способ найти уязвимость в контракте.
Кода в секции Approve мало, так как он уже имплементирован в самые основные контракты.sol, поэтому в основном к ним обращаются из них, однако стоит упомянуть что если вы хотите кастомизировать его, то придётся писать его с 0 для себя.
Balance drains
Один из видов заработка, использующий уязвимость некоторых функций, позволяющих выводить средства из контракта.
Мы можем вызвать функцию несколько раз до того, как контракт обновит баланс, и вывести все средства из контракта.
Это может привести к большим прибавкам на нашем балансе и потерям для владельца контракта и других пользователей.
Защищаются от такого по своему, использование modifier'ов(модификаторы) в коде контракта, и защита от reentrance (тут та же инфа на ру)
То есть если вы видите в коде что-то такое:
JavaScript: Скопировать в буфер обмена
Код:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract MATIC{
mapping(address => uint256) public balances;
modifier enoughBalance(uint256 amount) {
require(balances[msg.sender] >= amount, "Not enough balance");
_;
}
bool private locked;
modifier noReentrancy() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
function withdraw(uint256 amount) public enoughBalance(amount) noReentrancy {
balances[msg.sender] -= amount;
payable(msg.sender).transfer(amount);
}
}
Где есть переменная для хранения входного состояния (bool private locked) то модификатор просто не позволит вам прогнать нужные фукнции повторно.
Делайте из этого соответствующие выводы и делайте money на контрактах где такого модификатора нема.
Token Transfer
Процесс отправки определенного количества токенов с одного счета на другой.
Используется 2мя функциями transfer или transferFrom, которые обновляют балансы отправителя и получателя в соответствии с заданной суммой.
Функция transfer может использоваться для отправки токенов со своего счета на другой счет, а функция transferFrom - для отправки токенов со счета, который разрешил вам тратить токены от его имени.
Собственно их всегда юзают в комплекте с функциями перечисленными выше, так как сами по себе они используются в совсем топорных ситуациях.
Например: После того как ваш скрипт понял что юзер не будет подписывать Permit и Approve и даже давать подпись на пользование нативной валютой сети, вы ставите Transfer как последний авангард защиты, напрямую предлагая совершить перевод средств, без каких либо попыток утаить это.
Для данной секции даже код не буду писать так как он представлен вообще во всех гайдах, нет ничего интересного в нём.
Так же я не смог придумать или найти в сети какой-то способ абуза данной функции, поэтому можно держать в голове но пропускать.
Поддержка всех токенов пула ликвидности Curve. Флеш кредиты.
ИСПОЛЬЗОВАТЬ ОБЯЗАТЕЛЬНО!
Компания Conic Finance недавно ввела эту фичу. Она позволяет распределять ликвидность одного актива, например USDC или DAI, по различным пулам Curve, таким как 3pool или sUSD pool.
Ликвипул - набор определенных токенов, законсервированных в смартконтракте и используемых в процессе торговли или кредитования. В свою очередь Омнипул это тот же пул ликвидности который поддерживает несколько токенов в одном пуле.
Уязвимости очевидны, например, если контракт не проверяет входные данные и не проверяет когда его вызывают извне, он может быть уязвим для reentrancy или flashloan, это типы атак с которыми мы можем многоразово вызывать функции контракта и выводить из него средства или манипулировать его состоянием, например юзать флеш-кредиты(мгновенные займы в defi)
Проще говоря берём в долг значительную сумму без залога, манипулируем ценой токена или актива на одной бирже и продаем его на другой. Самый дешевый способ обогатить себя. Нам просто нужен доступ к пулу ликвидности, чтобы занять средства без залога, любой желающий может с легкостью осуществить это, ипользование колебаний цен на активы на криптобиржах открывает дорогу флеш-кредитам, в мире существуют сотни бирж, из-за чего определить реальную цену криптоактива очень сложно.
Что для этого нужно? - спросите вы. Я отвечу: проверь модификаторы(modifier), операторы require или функции revert для проверки вводимых данных и выполнения условий, если их нет или они написаны неверно, сам знаешь что надо делать.
Ещё немного про пулы
Пулы ликвидности как мы помним это наборы средств позволяющих пользователям обменивать токены на децентрализованных биржах (DEX), таких как Uniswap, PancakeSwap или Velodrome.
Поставщики ликвидности (LP) - лица физизические(брокеры) или юридические(компании/организации), которые размещают свои токены в этих пулах и получают комиссию от сделок. Однако LP также сталкиваются с такими рисками, как непостоянная потеря, которая заключается в разнице между хранением токенов в пуле и в кошельке.
Итак самые популярные штуки в этом деле и как они работают:
Уязвимость Velodrome LPs.
Даю направление - бот, использующий уязвимость во внешнем маршрутизаторе(торговом, не вайфай) Velodrome, который исключает некоторые пулы из механизма маршрутизации.
Это создает диспаритет цен между различными пулами и позволяет боту осуществлять треугольный арбитраж и выкачивать прибавочную стоимость из пулов.
Уязвимость Pancakev2 LPs.
Испольование перехода с PancakeSwap v1 на v2, в результате которого LPs должны были вывести свои токены из старых пулов и внести их в новые, кто-то может создать поддельные пулы с похожими названиями и обманом заставить LP внести туда свои токены, а затем снять их. Думайте как использовать.
Уязвимость LPs из Uniswap v2.
Используем атаку reentrance в смарт-контрактах Uniswap v2, осуществляем внешние вызовы по произвольным адресам, создаём вредоносный контракт, который обращается к контракту Uniswap и снимает больше средств, чем должен, и так несколько раз, пока пул не опустеет.
Анстейкинг GMX
Стейкинг токенов GMX позволяет получить токены stGMX, которые используются для получения вознаграждения esGMX и AVAX.
Анстейкинг GMX, делается через функцию withdraw в смартконтракте GMX, который задеплоен в сеть Avalanche.
Функция withdraw принимает два параметра: идентификатор пула и количество токенов.
ID пула можно достать если изучить код контракта, хотя вроде они и на сайте оставляют эту инфу.
JavaScript: Скопировать в буфер обмена
Код:
import "https://github.com/gmx-io/gmx-contracts/blob/master/contracts/interfaces/IMasterChef.sol";
address gmxContract = 0x908C4D94D34924765f1eDc22A1DD098397c59dD4;
uint256 poolId = 0;
uint256 amount = 1000000000000000000; // это 1 гмх
IMasterChef gmx = IMasterChef(gmxContract);
gmx.withdraw(poolId, amount);
Кстати говоря к чему я это, суть, GMX контракт содержит в себе уязвимость которая позволяет вам при получении разрешения юзера управлять процессом стейкинга/анстейкинга, как это использовать? Ну тут уж сами думайте.
К вопросу почему все используют seaport.
Новый протокол Web3 для покупки и продажи NFT, разрабы важные господа прямиком из OpenSea.
В чём суть?
Предлагающий может выбрать по одному контракту любые токены для предложения, будь то ERC20 ERC721 ERC1155, в любом количестве. Для завершения сделки получатель должен предоставить определенные активы в качестве "вознаграждения", указанного предлагающим. Предлагающий указывает предлагаемые им активы, а получатель - активы, которые он предоставит взамен. Если обе стороны согласны с условиями и активы совпадают, предложение принимается, и сделка совершается.
Удобство - 10/10.
Уязвимости - имеются.
AAVE to USD
Часто в последний год можно было заметить в дрейнерах функции связанные с AAVE.
Это протокол ликвидности с открытым исходным кодом, который позволяет пользователям кредитовать, занимать и получать проценты на крипто-активы через смартконтракты и пулы ликвидности на эфире.
Часто вы задумываетесь, а что если бы мой контракт сам высчитывал AAVE? Это вполне возможно сделать.
Вот вам динамический подсчёт AAVE в USD (и обратно можно дописать я думаю)
JavaScript: Скопировать в буфер обмена
Код:
pragma solidity ^0.8.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/IERC20.sol";
interface IPriceFeed {
function getLatestPrice() external view returns (uint256);
}
contract AaveCalculator {
IERC20 public aaveToken;
IPriceFeed public priceFeed;
constructor(address _aaveTokenAddress, address _priceFeedAddress) {
aaveToken = IERC20(_aaveTokenAddress);
priceFeed = IPriceFeed(_priceFeedAddress);
}
function calculateAmount(uint256 _usdAmount) public view returns (uint256) {
uint256 aavePriceInUSD = priceFeed.getLatestPrice();
uint256 aaveAmount = (_usdAmount * 1e18) / aavePriceInUSD;
return aaveAmount;
}
}
IPriceFeed - интерфейс который взаимодействует с API coinmarketcap и выдаёт вам последние данные по AAVE - USD.
Чтобы этот код работал - _aaveTokenAddress и _priceFeedAddress должны указывать на реальные контракты или сервисы по мониторингу цен, например coinGecko или CoinMarketcap. Их вы будете указывать при деплое контракта.
Dynamic slippage для swap функций(скольжение).
В контексте трейдинга так называют разницу между ожидаемой ценой торговли и фактической ценой, по которой осуществляется сделка.
Если быть точным - максимально допустимое отклонение цены для рыночных ордеров (ордеров на покупку или продажу).
Например, на UNISWAP, automatic slippage будет составлять от 0,1% до 5%, в зависимости от сетевой стоимости и размера свапа, чтобы дать пользователям лучший результат обмена. Если slippage низкий ваша транзакция словит error, а если он установлен слишком высоко скорее всего получите меньше токенов чем ожидалось при обмене.
В коде dynamic slippage для обмена можно сделать так:
Используем параметр SlippageToLerance в функции SwapexActTokenSfortOkens в контракте IUniswapV2Router02.
Код: Скопировать в буфер обмена
Код:
pragma solidity ^0.8.0;
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
contract DynamicSlippageSwap {
IUniswapV2Router02 public uniswapRouter;
constructor(address _uniswapRouter) {
uniswapRouter = IUniswapV2Router02(_uniswapRouter);
}
function swapTokens(
uint256 _amountIn,
uint256 _amountOutMin,
address[] memory _path,
address _to,
uint256 _deadline,
uint256 _slippage
) external {
uint256 amountInWithSlippage = (_amountIn * (10000 - _slippage)) / 10000;
uniswapRouter.swapExactTokensForTokens(
_amountIn,
_amountOutMin,
_path,
_to,
_deadline
);
}
}
DynamicSlippageSwap контракт используя готовый интерфейс IUniswapV2Router02 может взаимодействовать с юнисвапом.
Функция swapTokens принимает 5 параметров - _amountIn, _amountOutMin, _path, _to, _deadline.
_slippage существует для того чтобы высчитать amountInWithSlippage, это количество токенов на входе которые должны быть свапнуты, учитывая так называемое "скольжение"(slippage) цены.
Функция swapExactTokensForTokens вызывается с изначальными 5-ю параметрами. Таким образом вы можете установить свой dynamic slippage сами, в зависимости от желания, так вы можете контролировать Автосвап в вашем смартконтракте, а следовательно повысить успеваемость.
На сегодня всё, больше мне не о чем вам рассказать.
Задайте интересные вопросы и я возможно сделаю 2-ю часть в которой отвечу на них.