Foundry с 0: Cast команды. Часть 2
Сейчас существует достаточно большое количество Cast команд для самых разных задач: от простых запросов о получении баланса токена до работы с calldata и шифрованием.
Думаю, для начала пройдемся по тем, что чаще всего используют в своей работе, а оставшиеся будем изучать уже по мере необходимости.
Итак, мы можем открыть терминал и ввести команду:
Тут стоит сделать отступление и уточнить, что для выполнения некоторых команд, нам потребуется специальная RPC ссылка для доступа к блокчейн сетям. Получить такую бесплатно можно на таких популярных проектах как Alchemy или Infura.
По сути, процесс получения ссылок у них похож: регистрируетесь на портале, ищите кнопку Get API key или похожую со словом API, и потом генерируется ваша индивидуальная ссылка для rpc запросов. Выглядит она примерно так:
https://mainnet.infura.io/v3/apiKey
apiKey - ваш уникальный api ключ, показывать который нежелательно никому.
Там же можно получить ссылку на различные сети: Ethereum, Optimism, Arbitrum и т.д.
Именно эту ссылку и нужно будет добавлять в конце наших cast команд.
Если вы работаете в редакторе кода, а не в обычном терминале, то есть возможность создать .env файл и поместить туда ссылки, создав для них специальные переменные среды, и уже их использовать в cast командах.
Для небольшой тренировки, попробуем сегодня выполнить несколько команд для получения информации о сети.
Chain команды
Итак, нам доступны три команды:
cast chain-id
cast chain
cast client
Первая выдаст нам номер блокчейна (например, для Эфириума - это 1, для Оптимизма - 10), вторая - название сети, третья - клиента, через который отправляются запросы (например, для Infura - клиент GETH).
Для того чтобы выполнить команду в консоли потребуется прописать:
Также, думаю, сегодня можно рассмотреть и команды для блоков.
Block команды
Всего на данный момент их шесть:
https://book.getfoundry.sh/reference/cast/block-commands
1. cast find-block
2. cast gas-price
3. cast block-number
4. cast basefee
5. cast block
6. cast age
Первая ищет блок, который был ближе всего к нужной дате, например
Обратите внимание, что время указывается в формате unix (количество секунд прошедшее с момента 1 января 1970 года).
Вторая команда показывает текущую стоимость газа в нужной сети, например:
Пятая - получение информации о блоке, например:
earliest, finalized, safe, latest или pending.
При этом можно получить не всю информацию о блоке, а только необходимое поле. Попробуйте выполнить предыдущую команду, чтобы понять, о чем я говорю.
Получить информацию о конкретном поле можно с помощью модификации команды, а именно добавления --field опции:
Дальше мы продолжим говорить о командах cast и разберем другие примеры.
Задание
1. Зарегистрироваться на Alchemy или Infura;
2. Получить rpc ссылку;
3. Попробовать несколько команд из поста;
4. Настроить .env файл для rpc ссылок;
#foundry #cast #block #chain #lesson2
Сейчас существует достаточно большое количество Cast команд для самых разных задач: от простых запросов о получении баланса токена до работы с calldata и шифрованием.
Думаю, для начала пройдемся по тем, что чаще всего используют в своей работе, а оставшиеся будем изучать уже по мере необходимости.
Итак, мы можем открыть терминал и ввести команду:
cast helpона покажет нам все доступные cast команды.
Тут стоит сделать отступление и уточнить, что для выполнения некоторых команд, нам потребуется специальная RPC ссылка для доступа к блокчейн сетям. Получить такую бесплатно можно на таких популярных проектах как Alchemy или Infura.
По сути, процесс получения ссылок у них похож: регистрируетесь на портале, ищите кнопку Get API key или похожую со словом API, и потом генерируется ваша индивидуальная ссылка для rpc запросов. Выглядит она примерно так:
https://mainnet.infura.io/v3/apiKey
apiKey - ваш уникальный api ключ, показывать который нежелательно никому.
Там же можно получить ссылку на различные сети: Ethereum, Optimism, Arbitrum и т.д.
Именно эту ссылку и нужно будет добавлять в конце наших cast команд.
Если вы работаете в редакторе кода, а не в обычном терминале, то есть возможность создать .env файл и поместить туда ссылки, создав для них специальные переменные среды, и уже их использовать в cast командах.
Для небольшой тренировки, попробуем сегодня выполнить несколько команд для получения информации о сети.
Chain команды
Итак, нам доступны три команды:
cast chain-id
cast chain
cast client
Первая выдаст нам номер блокчейна (например, для Эфириума - это 1, для Оптимизма - 10), вторая - название сети, третья - клиента, через который отправляются запросы (например, для Infura - клиент GETH).
Для того чтобы выполнить команду в консоли потребуется прописать:
cast chain-id --rpc-url linkгде вместо link - указать вашу ссылку rpc, которую мы получали выше.
Также, думаю, сегодня можно рассмотреть и команды для блоков.
Block команды
Всего на данный момент их шесть:
https://book.getfoundry.sh/reference/cast/block-commands
1. cast find-block
2. cast gas-price
3. cast block-number
4. cast basefee
5. cast block
6. cast age
Первая ищет блок, который был ближе всего к нужной дате, например
cast find-block 1609459200покажет блок, который был ближе всего к Новому Году 2021.
Обратите внимание, что время указывается в формате unix (количество секунд прошедшее с момента 1 января 1970 года).
Вторая команда показывает текущую стоимость газа в нужной сети, например:
cast gas-price --rpc-url linkТретья - последний на данный момент блок:
cast block-number --rpc-url linkЧетвертая - basefee блока (кто не знает, что это, то почитайте про Лондонское обновление):
cast base-fee blockNum --rpc-url linkblockNum - это номер блока, basefee которого нам нужно получить. Вместо числа - 1, 443, 2343242 - можно указать одно из теговых значений: earliest, finalized, safe, latest или pending. По умолчанию идет latest.
Пятая - получение информации о блоке, например:
cast block finalized --rpc-url linkТут также можно указывать номер блока числом или одним из тегов:
earliest, finalized, safe, latest или pending.
При этом можно получить не всю информацию о блоке, а только необходимое поле. Попробуйте выполнить предыдущую команду, чтобы понять, о чем я говорю.
Получить информацию о конкретном поле можно с помощью модификации команды, а именно добавления --field опции:
cast block latest -f hash --rpc-url linkНу, и шестая команда служит для получения timestamp блока:
cast age blockNum --rpc-url linkгде blockNum это номер блока или его теговое значение.
Дальше мы продолжим говорить о командах cast и разберем другие примеры.
Задание
1. Зарегистрироваться на Alchemy или Infura;
2. Получить rpc ссылку;
3. Попробовать несколько команд из поста;
4. Настроить .env файл для rpc ссылок;
#foundry #cast #block #chain #lesson2
👍5🔥5
Foundry с 0. Часть 3
Продолжаем узнавать разнообразие cast команд и сегодня поговорим о тех, что помогают работать с аккаунтами, кошельками и транзакциями, и, на закуску, узнаем, как получить source код контракта с Etherscan.
Account команды
На данный момент существуют 6 команд cast для работы с аккаунтами:
1. cast balance - позволяет узнать количество нативных токенов сети на аккаунте в wei. Тут, кстати, можно уточнить номер блока, на момент которого был тот или иной баланс. Полная команда выглядит так:
Если при написании тестов нам будут требоваться эти команды, мы обязательно вернемся к ним и разберем подробнее.
4. cast nonce - тут все просто, получаем nonce аккаeнта, команда предельно простая:
P.S. В большинстве команд, где запросы идут в сеть, нужно добавлять свою rpc-url!
Теперь несколько слов о командах для работы с кошельком.
Wallet команды
1. cast wallet new - создать новый кошелек: адрес и приватный ключ.
2. cast wallet address - конвертация приватного ключа в адрес кошелька. Интересная команда, которая принимает достаточно большое количество опций для генерации адреса: на основе приватного ключа, mnemonic derivation path, mnemonic passphrase, обычного mnemonic. При этом вы также можете указывать, где хранится ключ, который нужно использовать для генерации адреса.
Самая простая команда звучит так:
https://book.getfoundry.sh/reference/cast/cast-wallet-address
3. cast wallet sign - с ней вы можете подписать сообщение с вашего кошелька. Также, помимо приватных ключей, можно использовать mnemonic опции. Простая команда выглядит так:
Далее несколько команд для работы с транзакциями.
Transaction команды
Для начала пройдемся по командам, которые позволяют получать информацию о проведенных транзакциях:
1. cast receipt - получения "чека" о транзакции. Потребуется указать хэш транзакции:
Вместо deposit - вы можете вставить calldata с селектором вызываемой функции и аргументами для нее.
Продолжаем узнавать разнообразие cast команд и сегодня поговорим о тех, что помогают работать с аккаунтами, кошельками и транзакциями, и, на закуску, узнаем, как получить source код контракта с Etherscan.
Account команды
На данный момент существуют 6 команд cast для работы с аккаунтами:
1. cast balance - позволяет узнать количество нативных токенов сети на аккаунте в wei. Тут, кстати, можно уточнить номер блока, на момент которого был тот или иной баланс. Полная команда выглядит так:
cast balance account --block blockNum --rpc-url RpcUrl2. cast storage - крутая команда, чтобы получить значения в storage контракта. Можно уточнить слот, в котором лежит значение, и скастовать информацию только из него. Например для WETH контракта:
cast storage 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 0или просто
cast storage 0x5Af0D9827E0c53E4799BB226655A1de152A425a53. cast proof - получение storage proof аккаунта. Не очень понял, что каких целей это может потребоваться. С этой командой вы получить proof таких полей как: accountProof, account address, account balance, codeHash, nonce, storageHash, storageProof, storageProof.key, storageProof.proof, storageProof.value.
Если при написании тестов нам будут требоваться эти команды, мы обязательно вернемся к ним и разберем подробнее.
4. cast nonce - тут все просто, получаем nonce аккаeнта, команда предельно простая:
cast nonce account5. cast code - получение байткода контракта. Пример с Weth:
cast code 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc26. cast codesize - получение runtime bytecode size контракта.
P.S. В большинстве команд, где запросы идут в сеть, нужно добавлять свою rpc-url!
Теперь несколько слов о командах для работы с кошельком.
Wallet команды
1. cast wallet new - создать новый кошелек: адрес и приватный ключ.
2. cast wallet address - конвертация приватного ключа в адрес кошелька. Интересная команда, которая принимает достаточно большое количество опций для генерации адреса: на основе приватного ключа, mnemonic derivation path, mnemonic passphrase, обычного mnemonic. При этом вы также можете указывать, где хранится ключ, который нужно использовать для генерации адреса.
Самая простая команда звучит так:
cast wallet address --private-key PRIVATE_KEYвесь список опция можно найти тут:
https://book.getfoundry.sh/reference/cast/cast-wallet-address
3. cast wallet sign - с ней вы можете подписать сообщение с вашего кошелька. Также, помимо приватных ключей, можно использовать mnemonic опции. Простая команда выглядит так:
cast wallet sign --private-key PRIV_KEY "hello"4. cast wallet verify - раз можно подписать сообщение, то есть возможность и проверки этого действия. С этой командой можно проверить адресата подписанного сообщения. Команда:
cast wallet verify --address account --address addressMessageSignature5. cast wallet vanity - интересная команда, которая позволяет сгенерировать адрес по специальным критериям: с определенным окончанием или началом, или nonce. Выглядит так:
cast wallet vanity --ends-with beefгде beef - это то, на что должен заканчиваться требуемый адрес.
Далее несколько команд для работы с транзакциями.
Transaction команды
Для начала пройдемся по командам, которые позволяют получать информацию о проведенных транзакциях:
1. cast receipt - получения "чека" о транзакции. Потребуется указать хэш транзакции:
cast receipt TX_HASH2. cast tx - похожая на первую и выдает информацию о транзакции.
cast tx TX_HASH3. cast-estimate - с ней можно оценить стоимость проведения транзакции по газу. Также можно указать, если с ней потребуется переслать нативную валюту, типа Эфира. Команда немного сложнее предыдущих и выглядит так:
cast estimate 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 --value 0.1ether "deposit()" --rpc-url rpcUrl0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 - адрес контракта, куда пойдет вызов, value - количество отправляемого с транзакцией Эфира, deposit - функция, которую будем вызывать.
Вместо deposit - вы можете вставить calldata с селектором вызываемой функции и аргументами для нее.
👍7❤1🔥1
4. cast run - команда, которая позволяет эмулировать транзакцию из сети на своей локальной сети, и расписать все traces (ее пути). Полезна для дебаггинга, поэтому будем обращаться к ней позже.
5. cast call - еще одна команда, которая сильно поможет при дебаггинге транзакций, так как выполняет вызов на адрес без публикации транзакции в сети. Много опций для тонкой настройки. Вернемся к ней в соответствующем уроке - посте.
6. cast publish - команда для публикации в сети предварительно подписанной транзакции. Выглядит так:
7. cast send - похожая на предыдущую команда, которая уже подписывает и публикует транзакцию в сети. Куча настроек. Вернемся к ней позже.
Ну, и последняя на сегодня, команда для получения кода контракта прямо с Etherscan. Тут вам потребуется предварительная регистрация на ресурсе и получение специального кода API. Команда выглядит так:
Завтра поговорим о cast командах для работы с ABI и другие полезные инструменты.
#foundry #cast #lesson3
5. cast call - еще одна команда, которая сильно поможет при дебаггинге транзакций, так как выполняет вызов на адрес без публикации транзакции в сети. Много опций для тонкой настройки. Вернемся к ней в соответствующем уроке - посте.
6. cast publish - команда для публикации в сети предварительно подписанной транзакции. Выглядит так:
cast publish --rpc-url RPC TXP.S. не помню, чтобы встречал ее практического применения. Будем разбираться.
7. cast send - похожая на предыдущую команда, которая уже подписывает и публикует транзакцию в сети. Куча настроек. Вернемся к ней позже.
Ну, и последняя на сегодня, команда для получения кода контракта прямо с Etherscan. Тут вам потребуется предварительная регистрация на ресурсе и получение специального кода API. Команда выглядит так:
cast etherscan-source 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 --etherscan-api-key ETHERSCAN_API_KEYПолучился довольно большой пост сегодня, но хотелось бы на этой недели пройти все cast команды, чтобы со следующей недели уже двигаться дальше.
Завтра поговорим о cast командах для работы с ABI и другие полезные инструменты.
#foundry #cast #lesson3
👍6🔥1
Foundry с 0. Часть 4
Сегодня мы закончим узнавать новые cast команды и пройдем последние три раздела: ABI, конвертация и полезные инструменты.
ABI команды
1. cast abi-encode / cast abi-decode - шифрует или расшифровывает данные. Например:
9. cast keccak - получить зашифрованные через keccak256 данные.
12. cast interface - создать интерфес контракта из ABI:
13. cast max-int / cast min-int / cast max-uint - получение максимальных значений uint256 / int256.
Команды для конвертации данных
Здесь очень много разных команд для конвертации, думаю, некоторые из них мы будем использовать при работе с тестами. В данном посте я просто представлю возможности данных cast команд.
Итак, вы можете конвертировать:
1. Бинарные данные в hex;
2. Fixed point number в integer;
3. Расшифровывать RLP данные;
4. UTF8 text в hex;
5. Количество wei в Эфир;
6. Получать адрес из bytes32;
7. Получать строку из bytes32;
8. hex в строку ASCII;
9. Из hex в числа;
10. Из hex в bytes32;
11. Из hex в RLP;
12. Совершать побитовые операции со сдвигом влево-вправо;
Полные список команд можно найти по ссылке:
https://book.getfoundry.sh/reference/cast/cast-shl
Крайне рекомендую попробовать поработать со всеми командами в свободное время, чтобы понять, как это все работает и привыкнуть работать с cast.
Задание
1. Попробовать выполнить 10 разны команд cast.
#foundry #cast #lesson4
Сегодня мы закончим узнавать новые cast команды и пройдем последние три раздела: ABI, конвертация и полезные инструменты.
ABI команды
1. cast abi-encode / cast abi-decode - шифрует или расшифровывает данные. Например:
cast abi-encode "someFunc(address,uint256)" 0x... 1 2342. cast 4byte - можно получить функцию по селектору.
cast abi-decode "balanceOf(address)(uint256)" 0x000000000000000000000000000000000000000000000000000000000000000a
cast 4byte 0x8cc5ce993. cast 4byte-decode - расшифровать функцию и ее аргументы из calldata:
cast 4byte-decode 0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d00000000000000000000000000000000000000000000000000174b37380cea0004. cast 4byte-event - получить функцию event из calldata
cast 4byte-event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5. cast calldata - создать calldata из функции и аргументов. Например,
cast calldata "someFunc(address,uint256)" 0x... 16. cast calldata-decode - расшифровать calldata
cast calldata-decode "transfer(address,uint256)" 0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0000000000000000000000000000000000000000000000000008a8e4b1a3d80007. cast-pretty-calldata - еще одна команда для расшифровки calldata, например:
cast pretty-calldata 0xa9059cbb000000000000000000000000e78388b4ce79068e89bf8aa7f218ef6b9ab0e9d00000000000000000000000000000000000000000000000000174b37380cea0008. cast upload-signature / cast sig - получить селектор функции:
cast upload-signature 'function approve(address,uint256)'можно указывать несколько подряд
9. cast keccak - получить зашифрованные через keccak256 данные.
cast keccak abcsdfg10. cast compute-address - сгенерировать адрес на основе nonce и адреса деплоера, например:
cast compute-address --nonce 0 --rpc-url yourUrl11. cast create2 - создать адрес через опкод creat2.
12. cast interface - создать интерфес контракта из ABI:
cast interface -o IWETH.sol 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2Также можно использовать ABI с Etherscan, но для этого придется получать API и использовать дополнительную опцию --etherscan-api-key.
13. cast max-int / cast min-int / cast max-uint - получение максимальных значений uint256 / int256.
Команды для конвертации данных
Здесь очень много разных команд для конвертации, думаю, некоторые из них мы будем использовать при работе с тестами. В данном посте я просто представлю возможности данных cast команд.
Итак, вы можете конвертировать:
1. Бинарные данные в hex;
2. Fixed point number в integer;
3. Расшифровывать RLP данные;
4. UTF8 text в hex;
5. Количество wei в Эфир;
6. Получать адрес из bytes32;
7. Получать строку из bytes32;
8. hex в строку ASCII;
9. Из hex в числа;
10. Из hex в bytes32;
11. Из hex в RLP;
12. Совершать побитовые операции со сдвигом влево-вправо;
Полные список команд можно найти по ссылке:
https://book.getfoundry.sh/reference/cast/cast-shl
Крайне рекомендую попробовать поработать со всеми командами в свободное время, чтобы понять, как это все работает и привыкнуть работать с cast.
Задание
1. Попробовать выполнить 10 разны команд cast.
#foundry #cast #lesson4
👍11❤1
Foundry с 0. Chisel. Часть 5
Сегодня, наконец, поговорим о Chisel.
По правде сказать, для меня он стал неким вызовом: материала по нему крайне мало, а описаний его практического применения и того меньше. Я потратил несколько дней, чтобы по частям собрать этот пост и показать возможности данной программы.
К слову сказать, после всех изысканий я примерно понял, почему так мало информации по нему. В целом, cast команды и remix ide могут полностью заменить его. Но для любителей работать в терминале, chisel может, вполне, стать новым инструментом.
Итак, chisel появляется у нас в системе вместе с установкой Foundry. Это также опенсорсный продукт, который разрабатывается, как я понял, сообществом, а не какой-либо коммерческой компанией.
Для того, чтобы начать с ним работать, достаточно открыть терминал и написать команду
Все доступные команды можно посмотреть, как это обычно бывает, с помощью
Из списка не особо понятно, что может делать chisel и как с ним обращаться, поэтому дадим несколько примеров.
1. Математические операции
С chisel можно выполнять не только математические операции, но также и побитовые.
Очень удобно получать значения вычислений побитовых сдвигов влево / вправо, или такие как "побитовое И" или "побитовое ИЛИ".
Достаточно в терминале написать запрос операции, типа:
2. Работа с ABI
Вы также можете получать зашифрованные данные через abi.encode и keccak256. Например,
Например, команда выше с abi.encode покажется как:
├ Hex (Tuple Encoded):
├─ Pointer ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000020
├─ Length ([0x20:0x40]): 0x00000000000000000000000000000000000000000000000000000000000000a0
└─ Contents ([0x40:..]): 0x000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000743686973656c2100000000000000000000000000000000000000000000000000
3. Переменные в chisel
В chisel можно создавать свои переменные и позже работать с ними. Например, вы можете создать переменную:
В chisel вы также можете создавать полноценные контракты и функции в них, а позже отслеживать пути работы!
Создадим простейший контракт прямо в терминале:
5. Получение интерфейсов контрактов с Etherscan
С помощью простой команды мы можем получить интерфейс любого контракта и сохранить его в файле. Однако тут есть ограничения: сохранить можно только интерфейс верифицированных контрактов в сети Ethereum, но вскоре обещают поддержку и других сетей. Команда простая:
Сегодня, наконец, поговорим о Chisel.
По правде сказать, для меня он стал неким вызовом: материала по нему крайне мало, а описаний его практического применения и того меньше. Я потратил несколько дней, чтобы по частям собрать этот пост и показать возможности данной программы.
К слову сказать, после всех изысканий я примерно понял, почему так мало информации по нему. В целом, cast команды и remix ide могут полностью заменить его. Но для любителей работать в терминале, chisel может, вполне, стать новым инструментом.
Итак, chisel появляется у нас в системе вместе с установкой Foundry. Это также опенсорсный продукт, который разрабатывается, как я понял, сообществом, а не какой-либо коммерческой компанией.
Для того, чтобы начать с ним работать, достаточно открыть терминал и написать команду
chiselМы войдем в режим работы программы.
Все доступные команды можно посмотреть, как это обычно бывает, с помощью
chisel !helpОбратите внимание, что тут все системные команды начинаются с восклицательного знака.
Из списка не особо понятно, что может делать chisel и как с ним обращаться, поэтому дадим несколько примеров.
1. Математические операции
С chisel можно выполнять не только математические операции, но также и побитовые.
Очень удобно получать значения вычислений побитовых сдвигов влево / вправо, или такие как "побитовое И" или "побитовое ИЛИ".
Достаточно в терминале написать запрос операции, типа:
100 ^ 4Единственное то, нужно быть аккуратными с делением, так как программа эмулирует работу с Solidity. Например, если попытаться выполнить операцию 3/2, то chisel не покажет результата.
100 << 5
2. Работа с ABI
Вы также можете получать зашифрованные данные через abi.encode и keccak256. Например,
abi.encode(256, bytes32(0), "Chisel!")или
keccak256(abi.encode(256, bytes32(0), "Chisel!"))Что классно с chisel, так это то, что он показывает, как это будет храниться в памяти (memory) с указание на поинтеры.
Например, команда выше с abi.encode покажется как:
├ Hex (Tuple Encoded):
├─ Pointer ([0x00:0x20]): 0x0000000000000000000000000000000000000000000000000000000000000020
├─ Length ([0x20:0x40]): 0x00000000000000000000000000000000000000000000000000000000000000a0
└─ Contents ([0x40:..]): 0x000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000743686973656c2100000000000000000000000000000000000000000000000000
3. Переменные в chisel
В chisel можно создавать свои переменные и позже работать с ними. Например, вы можете создать переменную:
uint a = 1;А потом попробовать выполнить математическую операцию, типа:
uint b = a << 0x08;4. Функции и контракты
В chisel вы также можете создавать полноценные контракты и функции в них, а позже отслеживать пути работы!
Создадим простейший контракт прямо в терминале:
contract Test {
function get() external view returns (uint) {
return 256;
}
}
затем потребуется сохранить его в переменную, чтобы можно было позже вызывать функции оттуда:Test t = new Test()Теперь мы можем обращаться к контракту, вызывая функции из него:
t.get()Более того, если мы добавим !traces перед вызовом функции, то сможем получить расшифровку "пути" вызова! Не большой аналог -vvv при тестах в Foundry, кто знает.
5. Получение интерфейсов контрактов с Etherscan
С помощью простой команды мы можем получить интерфейс любого контракта и сохранить его в файле. Однако тут есть ограничения: сохранить можно только интерфейс верифицированных контрактов в сети Ethereum, но вскоре обещают поддержку и других сетей. Команда простая:
!fetch 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 IWETHтут мы указываем адрес контракта, интерфейс которого хотим скачать и название для файла, который будет создан для этого.
👍3🔥1
6. Простое взаимодействие с загруженными контрактами
Также вы можете делать форк Ethereum и взаимодействовать с контрактами, которые уже были загружены в блокчейн. Для этого потребуется rpc-ссылка, которую можно получить, например, в сервисе Infura и выполнить команду:
usdc.balanceOf(...)
где в скобках указать интересующий адрес, как если бы это было в контракте!
7. Сохранение и загрузка сессий
Если вы хотите сохранить некоторые свои наработки в chisel, то можете выполнить команду !save для сохранения сессии, и позже загрузить ее через команду !load.
!clear поможет очистить память chisel от предыдущих операций.
8. Работа с памятью
Есть также три прекрасные команды !memdump, !stackdump и !rawstack для работы с памятью.
Так например, написав небольшой контракт или выполнив abi.encode можно посмотреть, как это будет располагаться в memory или стеке через соответствующую команду, что может быть очень полезно при дебаггинге или изучении работы памяти.
Заключение
Это то, что удалось узнать за пару дней изучения chisel. Вполне возможно, что "наворотов" и сценариев использования программы куда больше.
Мне он понравился тем, что можно быстро проверять побитовые операции и работать с памятью. Думаю, порой буду обращаться к нему.
А вы, что думаете про chisel?
#foundry #chisel #lesson5
Также вы можете делать форк Ethereum и взаимодействовать с контрактами, которые уже были загружены в блокчейн. Для этого потребуется rpc-ссылка, которую можно получить, например, в сервисе Infura и выполнить команду:
!fork yourRpcLinkможно также указать номер блока для форка:
!fork yourRpcLink 000000000Далее, к примеру, можно написать так:
interface IERC20 { function balanceOf(address holder) external virtual returns(uint256);}
и присвоить его в переменную с реальным контрактом:IERC20 usdc = IERC(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);и теперь вы можете делать вызов на контракт на эту функцию:
usdc.balanceOf(...)
где в скобках указать интересующий адрес, как если бы это было в контракте!
7. Сохранение и загрузка сессий
Если вы хотите сохранить некоторые свои наработки в chisel, то можете выполнить команду !save для сохранения сессии, и позже загрузить ее через команду !load.
!clear поможет очистить память chisel от предыдущих операций.
8. Работа с памятью
Есть также три прекрасные команды !memdump, !stackdump и !rawstack для работы с памятью.
Так например, написав небольшой контракт или выполнив abi.encode можно посмотреть, как это будет располагаться в memory или стеке через соответствующую команду, что может быть очень полезно при дебаггинге или изучении работы памяти.
Заключение
Это то, что удалось узнать за пару дней изучения chisel. Вполне возможно, что "наворотов" и сценариев использования программы куда больше.
Мне он понравился тем, что можно быстро проверять побитовые операции и работать с памятью. Думаю, порой буду обращаться к нему.
А вы, что думаете про chisel?
#foundry #chisel #lesson5
👍9🔥1
Foundry с 0. Часть 6
Во введении курса по Foundry, думаю, стоит пару слов сказать о настройках самой программы и различных опциях, которые вы можете задавать в начале пути тестирования своих смарт контрактов.
Вся конфигурация Foundry лежит в файле foundry.toml, который появляется в момент инициализации проекта и лежит в корневой папке.
Вообще, формат toml для меня был некоторым открытием при изучении Foundry. Вы знали, что это, по своей сути, отдельный язык?
Toml, или Tom's Obvious Minimal Language, это специальный язык для конфигурационных файлов, который просто читать и писать. Более того, из него достаточно просто парсить информацию!
В общем, если хотите узнать о нем чуть больше, рекомендую посетить его официальную страницу:
https://toml.io/en/
Но вернемся к Foundry.
Итак, все настройки лежат в файле foundry.toml. Если открыть его сразу после старта проекта, то можно увидеть что-то типа такого:
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
P.S. Актуально на момент написания поста. Среда развивается и вскоре могут появиться другие записи.
Настройки можно разделить на три категории: по умолчанию, общие и профильные.
profile.default - как можно догадаться, это профиль по умолчанию в Foundry. Скажем так, он самый главный.
Если вы создадите другие профили, например [profile.local], то тут можно указать какие-то специальные настройки для текущего проекта, которые в свою очередь, будут наследовать настройки из profile.default.
Смотрите, что получается. Где-то на своем компьютере вы можете создать корневой файл foundry.toml с дефолтным профилем и записать самые общие настройки там. А уже в каком-либо конкретном проекте - создавать новый файл foundry.toml с новым профилем для данного протокола и получать дефолтные настройки из главного файла, настроив предварительно пути.
На самом деле, я ни разу не видел такого применения, и для каждого проекта, который я аудировал, в корневой папке был дефолтный профиль. Но возможность есть, и о ней стоило рассказать.
Существует огромное количество настроек программы под свои нужды, попробую перечислить некоторые из них:
1. src - путь до контрактов от корня проекта;
2. test - путь до тестов от корня проекта;
3. out - путь до артефактов от корня проекта;
4. libs - массив путей с библиотеками от корня проекта;
5. cache - включить / выключить кэширования.
6. broadcast - путь для транслирования записей транзакций;
7. force - включение / выключение очистки кэша при сборке проекта;
8. solc_version - версия Solidity для компилятора;
9. ignored_error_codes - игнорировать ошибки в коде при сборке проекта;
10. evm_version - версия EVM, например london или byantium;
11. optimizer - вкл/выкл оптимизатора;
12. optimizer_runs - количество "пробегов" оптимизатора;
13. via_ir - вкл/выкл "прохода" компиляции через IR оптимизатор;
14. etherscan - установка параметров для работы с etherscan;
Более подробно об этих и множестве других настроек можно узнать отсюда:
https://book.getfoundry.sh/reference/config/
Для установки настроек в Foundry достаточно добавлять соответствующие записи в файл foundry.toml. Например, просто прописав:
optimizer = true
optimizer_runs = 20_000
Мы включим оптимизатор кода и установим его "пробег" на 20 000.
Надо также сказать, что какие-то уникальные настройки программы нам потребуются не так часто. По мере прохождения уроков по написанию тестов, я будут обращать ваше внимание на те или иные возможности конфигурации.
А если вы встретите какой-либо не понятный параметр в этом файле, то смело можете зайти на страницу официальной документации и через поиск решить свой вопрос.
#foundry #lesson6
Во введении курса по Foundry, думаю, стоит пару слов сказать о настройках самой программы и различных опциях, которые вы можете задавать в начале пути тестирования своих смарт контрактов.
Вся конфигурация Foundry лежит в файле foundry.toml, который появляется в момент инициализации проекта и лежит в корневой папке.
Вообще, формат toml для меня был некоторым открытием при изучении Foundry. Вы знали, что это, по своей сути, отдельный язык?
Toml, или Tom's Obvious Minimal Language, это специальный язык для конфигурационных файлов, который просто читать и писать. Более того, из него достаточно просто парсить информацию!
В общем, если хотите узнать о нем чуть больше, рекомендую посетить его официальную страницу:
https://toml.io/en/
Но вернемся к Foundry.
Итак, все настройки лежат в файле foundry.toml. Если открыть его сразу после старта проекта, то можно увидеть что-то типа такого:
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
P.S. Актуально на момент написания поста. Среда развивается и вскоре могут появиться другие записи.
Настройки можно разделить на три категории: по умолчанию, общие и профильные.
profile.default - как можно догадаться, это профиль по умолчанию в Foundry. Скажем так, он самый главный.
Если вы создадите другие профили, например [profile.local], то тут можно указать какие-то специальные настройки для текущего проекта, которые в свою очередь, будут наследовать настройки из profile.default.
Смотрите, что получается. Где-то на своем компьютере вы можете создать корневой файл foundry.toml с дефолтным профилем и записать самые общие настройки там. А уже в каком-либо конкретном проекте - создавать новый файл foundry.toml с новым профилем для данного протокола и получать дефолтные настройки из главного файла, настроив предварительно пути.
На самом деле, я ни разу не видел такого применения, и для каждого проекта, который я аудировал, в корневой папке был дефолтный профиль. Но возможность есть, и о ней стоило рассказать.
Существует огромное количество настроек программы под свои нужды, попробую перечислить некоторые из них:
1. src - путь до контрактов от корня проекта;
2. test - путь до тестов от корня проекта;
3. out - путь до артефактов от корня проекта;
4. libs - массив путей с библиотеками от корня проекта;
5. cache - включить / выключить кэширования.
6. broadcast - путь для транслирования записей транзакций;
7. force - включение / выключение очистки кэша при сборке проекта;
8. solc_version - версия Solidity для компилятора;
9. ignored_error_codes - игнорировать ошибки в коде при сборке проекта;
10. evm_version - версия EVM, например london или byantium;
11. optimizer - вкл/выкл оптимизатора;
12. optimizer_runs - количество "пробегов" оптимизатора;
13. via_ir - вкл/выкл "прохода" компиляции через IR оптимизатор;
14. etherscan - установка параметров для работы с etherscan;
Более подробно об этих и множестве других настроек можно узнать отсюда:
https://book.getfoundry.sh/reference/config/
Для установки настроек в Foundry достаточно добавлять соответствующие записи в файл foundry.toml. Например, просто прописав:
optimizer = true
optimizer_runs = 20_000
Мы включим оптимизатор кода и установим его "пробег" на 20 000.
Надо также сказать, что какие-то уникальные настройки программы нам потребуются не так часто. По мере прохождения уроков по написанию тестов, я будут обращать ваше внимание на те или иные возможности конфигурации.
А если вы встретите какой-либо не понятный параметр в этом файле, то смело можете зайти на страницу официальной документации и через поиск решить свой вопрос.
#foundry #lesson6
👍7🔥1
Foundry с 0. Часть 7
Постепенно переходя к практической части написания тестов, стоит также сделать небольшой пост про библиотеки Foundry, которые используются для этого.
На данный момент в Foundry используется набор стандартных библиотек - Forge Std, которые хранятся в папке lib в корне проекта. Всего их около 17 штук, но чаще всего используют всего 3-4 из них, а другие уже по умолчанию наследуются.
1. Test.sol - основная библиотека, которую мы наследуем в контракте-тесте. Она уже содержит в себе практически все остальные библиотеки.
2. console.sol или console2.sol - использование логирования данных, как в JavaScript через console.log().
3. Vm.sol - библиотеки для возможности использования специальных читкодов для управления локальным состоянием блокчейна.
4. Script.sol - базовые инструменты для написания скриптов.
5. StdAssertions - тестирование и сравнение результатов.
6. StdErrors - вывод ошибок.
7. StdStorage - работа с памятью при тестировании.
Более развернутую информацию по доступным библиотекам можно почитать тут:
https://book.getfoundry.sh/reference/forge-std/
Но, как я уже написал выше, в своей практике достаточно будет использовать Test.sol для первых этапов. Позже будем подключать и другие по мере необходимости.
#foundry #forgestd #lesson7
Постепенно переходя к практической части написания тестов, стоит также сделать небольшой пост про библиотеки Foundry, которые используются для этого.
На данный момент в Foundry используется набор стандартных библиотек - Forge Std, которые хранятся в папке lib в корне проекта. Всего их около 17 штук, но чаще всего используют всего 3-4 из них, а другие уже по умолчанию наследуются.
1. Test.sol - основная библиотека, которую мы наследуем в контракте-тесте. Она уже содержит в себе практически все остальные библиотеки.
2. console.sol или console2.sol - использование логирования данных, как в JavaScript через console.log().
3. Vm.sol - библиотеки для возможности использования специальных читкодов для управления локальным состоянием блокчейна.
4. Script.sol - базовые инструменты для написания скриптов.
5. StdAssertions - тестирование и сравнение результатов.
6. StdErrors - вывод ошибок.
7. StdStorage - работа с памятью при тестировании.
Более развернутую информацию по доступным библиотекам можно почитать тут:
https://book.getfoundry.sh/reference/forge-std/
Но, как я уже написал выше, в своей практике достаточно будет использовать Test.sol для первых этапов. Позже будем подключать и другие по мере необходимости.
#foundry #forgestd #lesson7
👍5🔥1
Foundry с 0. Часть 8
Сегодня мы поговорим о старте нового проекта на Foundry и подготовке рабочей среды.
P.S. Ранее, в нулевом разделе, был описан процесс установки программы на свой компьютер, поэтому дальше текст будет подразумевать, что он уже у вас имеется.
Итак, в самом начале нам нужно зайти в папку, в котором мы будем писать наш проект и выполнить команду:
.github - для работы с git;
lib - тут находятся контракты для импорта(например, OpenZeppelin);
noscript - вспомогательные скрипты для деплоя;
src - основная папка для наших контрактов;
test - папка для тестовых контрактов;
.foundry.toml - настройка для Foundry в данном проекте;
Свой проект мы можем писать, создавая новые файлы в папке src, а тесты - в test. Я проговариваю это еще раз, так как видел проекты, в которых разработчики создавали отдельные папки все src для контрактов, а потом жаловались, что что-то не работает.
Старайтесь держать структуру файлов в корневой папке.
Если же вы получили готовый проект для написания тестов и скачали его с GitHub, то подготовить его в своей рабочей среде можно выполнив команду:
Да, несколько слов о путях к файлам.
В некоторых проектах можно встретить отдельный файл remappings.txt. Именно в нем вы можете прописывать уникальные пути к файлам. Например, после установки контрактов от Open Zeppelin, они будут находиться в папке lib и иметь полный путь:
Это бывает удобно, когда у вас большая файловая система в проекте.
Также, если вы видите ошибку в контракте, когда редактор подчеркивает импорты, то одним из первых действий желательно проверять эти пути в этом файле, так как у разработчиков они могут быть прописаны иначе от вашей установки.
#foundry #lesson8
Сегодня мы поговорим о старте нового проекта на Foundry и подготовке рабочей среды.
P.S. Ранее, в нулевом разделе, был описан процесс установки программы на свой компьютер, поэтому дальше текст будет подразумевать, что он уже у вас имеется.
Итак, в самом начале нам нужно зайти в папку, в котором мы будем писать наш проект и выполнить команду:
forge initОна создаст несколько файлов и папок, в которых мы уже и будем дальше учиться писать тесты. Пройдемся по ним:
.github - для работы с git;
lib - тут находятся контракты для импорта(например, OpenZeppelin);
noscript - вспомогательные скрипты для деплоя;
src - основная папка для наших контрактов;
test - папка для тестовых контрактов;
.foundry.toml - настройка для Foundry в данном проекте;
Свой проект мы можем писать, создавая новые файлы в папке src, а тесты - в test. Я проговариваю это еще раз, так как видел проекты, в которых разработчики создавали отдельные папки все src для контрактов, а потом жаловались, что что-то не работает.
Старайтесь держать структуру файлов в корневой папке.
Если же вы получили готовый проект для написания тестов и скачали его с GitHub, то подготовить его в своей рабочей среде можно выполнив команду:
forge installОна установит необходимые зависимости и настроит пути.
Да, несколько слов о путях к файлам.
В некоторых проектах можно встретить отдельный файл remappings.txt. Именно в нем вы можете прописывать уникальные пути к файлам. Например, после установки контрактов от Open Zeppelin, они будут находиться в папке lib и иметь полный путь:
lib/openzeppelin-contracts/contracts/token/ERC20.solВы можете модифицировать его, сократив до
@openzeppelin/token/ERC20.solДля этого, в файле remappings.txt следует прописать:
@openzeppelin/ = lib/openzeppelin-contracts/contracts/И теперь "под капотом" он будет заменяться на более короткий.
Это бывает удобно, когда у вас большая файловая система в проекте.
Также, если вы видите ошибку в контракте, когда редактор подчеркивает импорты, то одним из первых действий желательно проверять эти пути в этом файле, так как у разработчиков они могут быть прописаны иначе от вашей установки.
#foundry #lesson8
❤5🔥1
Foundry с 0. Часть 9
На следующей неделе мы понемногу начнем разбирать уже практическую часть написания тестов на Foundry. А перед этим нужно рассказать, какие тесты вообще можно делать для своего проекта.
1. Unit тест
Самый простой вид тестирования. По сути, мы проверяем, что функция выполняет свою роль и выдает результат, который должен считаться валидным.
2. Fuzz тест
Немного сложно перевести основную идею этого вида тестирования, поэтому его чаще всего и называют фаззинг. Это такой вид тестов, когда сам Foundry подставляет аргументы в тестируемую функцию и пытается найти такие, которые могут сломать ее.
Грубый пример, у нас есть функция перевода, которая допускает значения меньше uint112. И вот мы, как бы говорим, Foundry найди такие значения, чтобы функция откатилась. Он в аргументы подставляет разные значения от uint4 до uint256 и выдает результат.
Если сейчас не понятно, то с практикой вы лучше осознаете всю ее пользу.
3. Invariant тесты
Сравнительно недавнее веяние в мире тестов. Активно его стали развивать только в последние полгода.
Инварианты - это такие значение, которые всегда должны быть "правильными"... Я сам долго пытался понять это, но объяснить лучше на примере.
Вот вы запустили свой токен. И вот его totalSupply никогда не должен быть больше общего количества токенов у пользователей. Или, что количество токенов на вывод не должно превышать количество токенов при депозите.
Такие параметры сложно проверить из-за того, что при первых двух видах тестов состояния блокчейна обнуляется каждый раз, а в данном случае оно должно "запоминаться" на изменения. Подробнее об этом в постах с соответствующей темой.
4. Differential тесты
Более сложные по своей основе тесты, которые иногда еще называют differential fuzzing.
Это такой метод тестирования, который включает в себя исполнение различных реализаций одной и той же функции или логики, с последующим сравнение их результатов.
Я сам не писал еще такие тесты, поэтому будем позже разбираться с ними вместе.
5. Fork тесты
Это тесты ваших контрактов на "скопированном" участке реальной сети блокчейна. Такое тестирование бывает полезно, когда вы хотите узнать, будет ли ваш код работать на различных версиях EVM.
6. Дебаггинг
Возможно, не совсем тестирование, но это мощный инструмент для работы со смарт контрактами и отслеживании путей работы ваших функций. Уверен, что каждый хороший разработчик на одном из этапов своего обучения задавался целью как можно лучше освоить эту тему.
В рамках каждого вида тестов мы можем проверять различные условия в коде (if/else, require), математические операции, порождение событий и вообще практически все, что захотим.
В помощь этому был создан прекрасный инструмент forge coverage, который показывает участки кода требующие проведения тестирования и само покрытие тестами всего контракта.
Со следующей неделе мы начнем разбирать практическую часть написание тестов. А впереди у нас еще много работы!
#foundry #lesson9
На следующей неделе мы понемногу начнем разбирать уже практическую часть написания тестов на Foundry. А перед этим нужно рассказать, какие тесты вообще можно делать для своего проекта.
1. Unit тест
Самый простой вид тестирования. По сути, мы проверяем, что функция выполняет свою роль и выдает результат, который должен считаться валидным.
2. Fuzz тест
Немного сложно перевести основную идею этого вида тестирования, поэтому его чаще всего и называют фаззинг. Это такой вид тестов, когда сам Foundry подставляет аргументы в тестируемую функцию и пытается найти такие, которые могут сломать ее.
Грубый пример, у нас есть функция перевода, которая допускает значения меньше uint112. И вот мы, как бы говорим, Foundry найди такие значения, чтобы функция откатилась. Он в аргументы подставляет разные значения от uint4 до uint256 и выдает результат.
Если сейчас не понятно, то с практикой вы лучше осознаете всю ее пользу.
3. Invariant тесты
Сравнительно недавнее веяние в мире тестов. Активно его стали развивать только в последние полгода.
Инварианты - это такие значение, которые всегда должны быть "правильными"... Я сам долго пытался понять это, но объяснить лучше на примере.
Вот вы запустили свой токен. И вот его totalSupply никогда не должен быть больше общего количества токенов у пользователей. Или, что количество токенов на вывод не должно превышать количество токенов при депозите.
Такие параметры сложно проверить из-за того, что при первых двух видах тестов состояния блокчейна обнуляется каждый раз, а в данном случае оно должно "запоминаться" на изменения. Подробнее об этом в постах с соответствующей темой.
4. Differential тесты
Более сложные по своей основе тесты, которые иногда еще называют differential fuzzing.
Это такой метод тестирования, который включает в себя исполнение различных реализаций одной и той же функции или логики, с последующим сравнение их результатов.
Я сам не писал еще такие тесты, поэтому будем позже разбираться с ними вместе.
5. Fork тесты
Это тесты ваших контрактов на "скопированном" участке реальной сети блокчейна. Такое тестирование бывает полезно, когда вы хотите узнать, будет ли ваш код работать на различных версиях EVM.
6. Дебаггинг
Возможно, не совсем тестирование, но это мощный инструмент для работы со смарт контрактами и отслеживании путей работы ваших функций. Уверен, что каждый хороший разработчик на одном из этапов своего обучения задавался целью как можно лучше освоить эту тему.
В рамках каждого вида тестов мы можем проверять различные условия в коде (if/else, require), математические операции, порождение событий и вообще практически все, что захотим.
В помощь этому был создан прекрасный инструмент forge coverage, который показывает участки кода требующие проведения тестирования и само покрытие тестами всего контракта.
Со следующей неделе мы начнем разбирать практическую часть написание тестов. А впереди у нас еще много работы!
#foundry #lesson9
👍7
Foundry с 0. Часть 10
Мы закончили с введением в Foundry и скоро начнем изучать написание тестов.
Понимаю, что для многих текстовой формат может не подходит, поэтому выкладываю ссылки на несколько крутых ресурсов с Ютуб, которые помогли мне в свое время.
Два видео на русском языке:
1. Foundry: альтернатива Hardhat для разработки/тестов
2. Нечёткое тестирование (fuzzy testing) в Foundry
И несколько на английском:
3. Learn Solidity, Blockchain Development, & Smart Contracts
4. Introduction | Testing with Foundry
5. How to Foundry with Brock Elmore
6. Testing with Foundry
7. Fuzz & Invariant Tests
8. How to Foundry 2.0: Brock Elmore
Этих видео будет достаточно для того, чтобы погрузиться в Foundry и самому начать писать тесты.
Всем приятных выходных и легкого обучения!
#foundry #lesson10
Мы закончили с введением в Foundry и скоро начнем изучать написание тестов.
Понимаю, что для многих текстовой формат может не подходит, поэтому выкладываю ссылки на несколько крутых ресурсов с Ютуб, которые помогли мне в свое время.
Два видео на русском языке:
1. Foundry: альтернатива Hardhat для разработки/тестов
2. Нечёткое тестирование (fuzzy testing) в Foundry
И несколько на английском:
3. Learn Solidity, Blockchain Development, & Smart Contracts
4. Introduction | Testing with Foundry
5. How to Foundry with Brock Elmore
6. Testing with Foundry
7. Fuzz & Invariant Tests
8. How to Foundry 2.0: Brock Elmore
Этих видео будет достаточно для того, чтобы погрузиться в Foundry и самому начать писать тесты.
Всем приятных выходных и легкого обучения!
#foundry #lesson10
👍3🔥3❤1
Foundry с 0. Часть 11
Сегодня мы начинаем практиковаться с тестами и пойдем также с самого начала. Мы разберем все "по кирпичикам", постепенно усложняя и материал и тесты.
Что нужно знать перед написанием тестов?
Во-первых, все тесты пишутся в папке test. Можно, конечно, создавать новые папки, но придется переписывать пути для Foundry и производить другие не нужные манипуляции. Поэтому проще все делать уже в одной папке, в которой строить нужную нам архитектуру.
Во-вторых, по правилам Foundry название файлов для тестов должно значится как fileName.t.sol. Другими словами окончание t.sol обязательно для каждого файла теста.
В-третьих, практически в каждом файле тестов нам нужно будет импортировать библиотеку Test от Forge и контракт, для которого будут писаться тесты:
Наш контракт:
Далее нам нужно будет создать объект нашего контракта и поместить его в переменную.
Для этого есть специальная служебная функция setUp(). Если вы переходите на Foundry с Hardhat, то можете представить себе setUp() как нечто подобное loadFixture или beforeEach. Другими словами setUp() будет исполняться перед каждым отдельным тестом. Это нужно для того, чтобы наш локальный блокчейн, на котором и будут проходить все тесты, не хранил изменения в стейте, а очищал его. Так сами тесты будут более достоверными.
Итак, в setUp() мы будем разворачивать наш контракт Counter:
Самое главное, что нужно запомнить сейчас так это то, что название тестов:
1. Должны начинаться со слова test;
2. Должны кратко описывать то, что они тестируют;
В нашем случае мы можем создать тест с названием:
Итак, теперь нам нужно понять логику проведения тестов. По мере постов, вы более подробно поймете, о чем идет речь, поэтому сейчас напишу по простому.
Смотрите, у нас есть функция, которую мы хотим протестировать. Результатом работы этой функции будет какое-то значение или действие: изменение переменной, баланса, голосов, доступа или чего-то еще. Т.е. мы ожидаем определенные результаты.
В тестах нам и нужно сравнить наши ожидания с тем, что явилось результатом исполнения функции. Например, у нас в контракте есть функция increment(). Что она делает? Она прибавляет 1 к значению в number.
Как написать для этого тест?
Теперь нам нужно обратиться к Foundry в терминале и попросить сделать тест. Это делается через команду:
Сегодня мы начинаем практиковаться с тестами и пойдем также с самого начала. Мы разберем все "по кирпичикам", постепенно усложняя и материал и тесты.
Что нужно знать перед написанием тестов?
Во-первых, все тесты пишутся в папке test. Можно, конечно, создавать новые папки, но придется переписывать пути для Foundry и производить другие не нужные манипуляции. Поэтому проще все делать уже в одной папке, в которой строить нужную нам архитектуру.
Во-вторых, по правилам Foundry название файлов для тестов должно значится как fileName.t.sol. Другими словами окончание t.sol обязательно для каждого файла теста.
В-третьих, практически в каждом файле тестов нам нужно будет импортировать библиотеку Test от Forge и контракт, для которого будут писаться тесты:
import {Test, console2} from "forge-std/Test.sol";
В четвертых, уже наш тестовый контракт обязательно должен иметь наследование от Test:contract myContract is Test {}
Возьмем, к примеру, файл контракта и файл теста, которые создаются при инициализации проекта: Counter.sol:Наш контракт:
contract Counter {
uint256 public number;
function setNumber(uint256 newNumber) public {
number = newNumber;
}
function increment() public {
number++;
}
}
И напишем к нему простые тесты. Для начала импортируем библиотеку Test и наш контракт, а также создаем наследование:// SPDX-License-Identifier: UNLICENSEDИз Test, как вы видите, мы получаем два объекта: сам Test и еще Console. Второй объект поможет нам логировать данные из тестов и выводить их в консоль, очень похоже на console.log из javanoscript.
pragma solidity ^0.8.13;
import {Test, console2} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";
contract CounterTest is Test {}
Далее нам нужно будет создать объект нашего контракта и поместить его в переменную.
Counter public counter;Но как поместить объект контракта в переменную при том, что в Foundry нет конструктора?
Для этого есть специальная служебная функция setUp(). Если вы переходите на Foundry с Hardhat, то можете представить себе setUp() как нечто подобное loadFixture или beforeEach. Другими словами setUp() будет исполняться перед каждым отдельным тестом. Это нужно для того, чтобы наш локальный блокчейн, на котором и будут проходить все тесты, не хранил изменения в стейте, а очищал его. Так сами тесты будут более достоверными.
Итак, в setUp() мы будем разворачивать наш контракт Counter:
setUp() public {
counter = new Counter();
}
Теперь можно написать наш первый тест. Самое главное, что нужно запомнить сейчас так это то, что название тестов:
1. Должны начинаться со слова test;
2. Должны кратко описывать то, что они тестируют;
В нашем случае мы можем создать тест с названием:
function test_Increment() public {}
Также следует запомнить, что все функции для тестов должны иметь область видимости public или external, чтобы программа могла работать с ними.Итак, теперь нам нужно понять логику проведения тестов. По мере постов, вы более подробно поймете, о чем идет речь, поэтому сейчас напишу по простому.
Смотрите, у нас есть функция, которую мы хотим протестировать. Результатом работы этой функции будет какое-то значение или действие: изменение переменной, баланса, голосов, доступа или чего-то еще. Т.е. мы ожидаем определенные результаты.
В тестах нам и нужно сравнить наши ожидания с тем, что явилось результатом исполнения функции. Например, у нас в контракте есть функция increment(). Что она делает? Она прибавляет 1 к значению в number.
Как написать для этого тест?
function test_Increment() public {
counter.increment();
assertEq(counter.number(), 1);
//console2.log(counter.number());
}
В начале мы вызываем функцию в нашем контракте для изменения переменной number, а затем проверяем, что наше ожидаемое значение будет равно значению, которое сохранилось в переменной. С помощью console2.log мы смотрим значение number.Теперь нам нужно обратиться к Foundry в терминале и попросить сделать тест. Это делается через команду:
👍1
forge testТакже вы можете получить больше информации о транзакциях в тестах, уточнив команду до:
forge test -vvВсего 5 уровней уточнений:
1. По умолчанию срабатывает при обычно команде тестов;
2. -vv - для простого отображения ошибок и console.log;
3. -vvv - для отображения путей теста;
4. -vvvv - более подробное отображения действий теста;
5. -vvvvv - также подробное отображение путей;
Последние два часто применяются при дебагинге.
Итак, прописываем команду и видим, что тест прошел, о чем символизирует надпись [PASS] напротив теста в терминале.
P.S. Что интересно, так это то, что, если раскомментировать console2.log, то он будет показывать значение 0, хотя тест будет пройден!
Если же мы подправим наш тест до:
assertEq(counter.number(), 2);То после команды в терминале, увидим надпись [FAIL] и информацию об ошибке.
Так, думаю, на сегодня этого достаточно, получился какой-то большой пост. А завтра поговорим о других командах для проверки результатов функций.
#foundry #lesson11
👍9
Foundry с 0. Часть 12
Продолжаем тему предыдущего поста и поговорим вообще о вариантах сравнения ожидаемых результатов исполнения функции в тестах с полученными.
Тогда мы узнали о первой функции-помощнике - asserteq(), которая принимает два значения (ожидаемое и полученное) и сравнивает их на равенство. Другими словами, в данном случае, чтобы тест прошел они должны быть равны друг другу.
При этом есть еще множество других сравнений. Например, создадим новый тест, в котором установим переменную number = 150.
assertGt(counter.number(), 140); //проверка, если "а" больше "б";
assertGe(counter.number(), 150); // проверка, если "а" больше или равно "б";
assertLt(counter.number(), 170); // проверка, если "а" меньше "б";
assertLe(counter.number(), 150); // проверка, если "а" меньше или равно "б";
Есть еще две более сложные команды:
assertApproxEqAbs();
assertApproxEqRel();
Первая проверяет, что значение "а" примерно равно "б", но в пределах определенной дельты. Другими словами, "а" не должно превышать "б+с", например:
Кроме того, вы также можете проверять еще несколько параметров и не только числовых:
1. assertTrue(); - проверка, что результат будет равен true
2. assertFalse(); - проверка, что результат будет равен false
3. assertEqDecimal(); - проверка, на равенство decimals
4. assertEq32(); - проверка, что результат будет равен bytes32
5. assertEq0(); - проверка, что результат будет равен bytes32
Хоть они используют реже, чем остальные, все равно их также можно встретить в крупных проектах.
#foundry #lesson12
Продолжаем тему предыдущего поста и поговорим вообще о вариантах сравнения ожидаемых результатов исполнения функции в тестах с полученными.
Тогда мы узнали о первой функции-помощнике - asserteq(), которая принимает два значения (ожидаемое и полученное) и сравнивает их на равенство. Другими словами, в данном случае, чтобы тест прошел они должны быть равны друг другу.
При этом есть еще множество других сравнений. Например, создадим новый тест, в котором установим переменную number = 150.
function test_setNumber() public {
counter.setNumber(150);
}
Теперь с помощью последующих команды мы можем проверить, что:assertGt(counter.number(), 140); //проверка, если "а" больше "б";
assertGe(counter.number(), 150); // проверка, если "а" больше или равно "б";
assertLt(counter.number(), 170); // проверка, если "а" меньше "б";
assertLe(counter.number(), 150); // проверка, если "а" меньше или равно "б";
Есть еще две более сложные команды:
assertApproxEqAbs();
assertApproxEqRel();
Первая проверяет, что значение "а" примерно равно "б", но в пределах определенной дельты. Другими словами, "а" не должно превышать "б+с", например:
assertApproxEqAbs(counter.number(), 200, 200);Вторая же команда делает примерно тоже, но в процентном соотношении, где 100% = 1е18, и пример может выглядеть так:
assertApproxEqRel(counter.number(), 150, 0.4e18);Эти две команды могут быть полезны при работе с DeFi протоколами, где часто идет работа с дельтами значений и ценами на токены.
Кроме того, вы также можете проверять еще несколько параметров и не только числовых:
1. assertTrue(); - проверка, что результат будет равен true
2. assertFalse(); - проверка, что результат будет равен false
3. assertEqDecimal(); - проверка, на равенство decimals
4. assertEq32(); - проверка, что результат будет равен bytes32
5. assertEq0(); - проверка, что результат будет равен bytes32
Хоть они используют реже, чем остальные, все равно их также можно встретить в крупных проектах.
#foundry #lesson12
👍1
Foundry с 0. Часть 13
Вместе с тем, что в своих тестах мы проверяем полученные значение к ожидаемым, нам требуется также проверять и правильную отработку ошибок и событий. Давайте поговорим, как это происходит.
Для начала добавим новую функцию в наш контракт Counter.sol:
Как это протестировать?
Если мы просто хотим убедиться, что данная функция не сработает, если число будет отличное от 100, то можно сделать так:
Когда мы пишем testFail, необходимо чтобы транзакция упала - тогда тест пройдет. Если же транзакция завершится нормально, то тест будет "завален". Тут, скажем, немного обратная от привычной логика.
Чуть позже мы увидим более практическое ее применение, а сейчас поговорим, как отслеживать вызов ошибок при тестах.
Как протестировать require и условия с кастомными ошибками?
В Foundry есть так называемые читкоды, которые сильно облегчаю работу с тестами и взаимодействие с локальным блокчейном. Вместе с библиотекой Test мы можем использовать огромное количество читов.
Одним из них является:
Далее посмотрим на два вида кастомных ошибок.
Для начала добавим их и две новые функции в контракт:
Я видел в нескольких видео, а также в официальной документации, что порой для кастомных ошибок можно использовать более простые конструкции, типа:
Еще интереснее дела обстоят с порождением событий и их тестированием.
Объявим его в нашем контракте Counter:
Далее нам нужно породить это событие в нашем тесте и уже после вызвать функцию.
Немного странно, но так это работает.
Вместе с тем, что в своих тестах мы проверяем полученные значение к ожидаемым, нам требуется также проверять и правильную отработку ошибок и событий. Давайте поговорим, как это происходит.
Для начала добавим новую функцию в наш контракт Counter.sol:
function setNumber100(uint256 newNumber) public {
require(newNumber == 100, 'Wrong numer!');
number = newNumber;
}
Она просто проверяет, чтобы значение для установки в переменную было обязательно равно 100, иначе происходит откат транзакции.Как это протестировать?
Если мы просто хотим убедиться, что данная функция не сработает, если число будет отличное от 100, то можно сделать так:
function testFail_setNumber100() public {
counter.setNumber100(150);
}
Обратите внимание на testFail. Так мы говорим Foundry, что ожидаем откат транзакции. Другими словами, если сейчас транзакция не пройдет, то результатом будет fail и таким образом тест удастся. Поняли, в чем дело?Когда мы пишем testFail, необходимо чтобы транзакция упала - тогда тест пройдет. Если же транзакция завершится нормально, то тест будет "завален". Тут, скажем, немного обратная от привычной логика.
Чуть позже мы увидим более практическое ее применение, а сейчас поговорим, как отслеживать вызов ошибок при тестах.
Как протестировать require и условия с кастомными ошибками?
В Foundry есть так называемые читкоды, которые сильно облегчаю работу с тестами и взаимодействие с локальным блокчейном. Вместе с библиотекой Test мы можем использовать огромное количество читов.
Одним из них является:
vm.expectRevert();Тут мы как бы обращаемся к vm (virtual machine) и говорим, что ожидаем реверт с некоторым сообщением. Наш тест может выглядеть так:
function test_setNumber_Revert() public {
vm.expectRevert("Wrong numer!");
counter.setNumber100(150);
}
Обратите внимание, что читкод мы пишем до исполнения функции, а не после нее. Это очень важно для хода выполнения тестов! Более того, в этом случае мы пишем название теста просто test_testName, а не testFail_testName, т.е. слово fail тут уже не нужно. Далее посмотрим на два вида кастомных ошибок.
Для начала добавим их и две новые функции в контракт:
error WrongNum(address caller, uint256 num);Тесты для обеих функций могут выглядеть так:
error WrongSet();
function setNumber150(uint256 newNumber) public {
if (newNumber != 150) revert WrongSet();
number = newNumber;
}
function setNumber200(uint256 newNumber) public {
if (newNumber != 200) revert WrongNum(msg.sender, newNumber);
number = newNumber;
}
function test_setNumber_Revert150() public {
vm.expectRevert(abi.encodeWithSignature('WrongSet()'));
counter.setNumber150(200);
}
function test_setNumber_Revert200() public {
vm.expectRevert(abi.encodeWithSignature("WrongNum(address,uint256)", address(this),150));
counter.setNumber200(150);
}
Ошибки и их аргументы мы оборачиваем в abi.encodeWithSignature() и это становится нашим ожидаемым результатом тестов.Я видел в нескольких видео, а также в официальной документации, что порой для кастомных ошибок можно использовать более простые конструкции, типа:
function test_setNumber_Revert150() public {
vm.expectRevert('WrongSet.selector);
counter.setNumber150(200);
}
Оба варианта рабочие. Можете смело использовать их в своих тестах.Еще интереснее дела обстоят с порождением событий и их тестированием.
Объявим его в нашем контракте Counter:
event NewEvent(uint256 num);и добавим запись в функцию increment() в виде:
emit NewEvent(number);Наш тест для событий может выглядеть так:
function test_IncrementEvent() public {
vm.expectEmit(true, true, true, false);
emit NewEvent(counter.number());
counter.increment();
}
Сначала мы используем новый читкод vm.expectEmit(), который принимает четыре аргумента в качестве булевых значение. Первые три устанавливаются для отслеживания indexed параметров в событии, последний - нужна ли проверка входных значений. Далее нам нужно породить это событие в нашем тесте и уже после вызвать функцию.
Немного странно, но так это работает.
👍2
Обратите внимание, для того чтобы протестировать порождение событий в контракте-тесте также нужно создать такой же event!
В конце неделе я выложу на канал итоговый сборный файл тестов и контракта, который мы напишем, чтобы бы вы могли сами взглянуть на полный код и попрактиковаться.
#foundry #lesson13
В конце неделе я выложу на канал итоговый сборный файл тестов и контракта, который мы напишем, чтобы бы вы могли сами взглянуть на полный код и попрактиковаться.
#foundry #lesson13
👍1🔥1
Foundry с 0. Часть 14
В прошлом посте мы впервые встретились с читкодами, помните vm.expectRevert() и vm.expectEmit()?
Читкоды Foundry это специальные служебные функции в программе, которые помогают нам писать качественные тесты и манипулировать состоянием локального блокчейна! Вообще это очень крутая штука.
В течение цикла постов мы будем много раз встречать с ними, но для начала стоит разбить их по нескольким категориям использования.
1. Манипуляции локальным блокчейном
С помощью этой категории вы можете управлять состоянием блокчейна для своих тестов. Например, вы можете "перемотать время" или перескочить некоторое количество блоков, пополнить свои счета токенов и nft, установить block.difficulty или block.basefee, управлять nonce и многое другое.
2. Assertions
Другая категория, которая позволяет проводить сравнения ожидаемых результатов действия функции с полученными, примером подобных функций могут служить те же vm.expectRevert() и vm.expectEmit().
3. Форк сетей
Категория функций для работы с форками реальных сетей блокчейна для ваших тестов. Бывает крайне полезно оценить работу контракта на всех сетях, куда планируется загрузка!
4. Работа с переменными среды
Вы можете получать значения и обрабатывать данные из переменных среды, которые помогают управлять разработкой проекта.
5. Функции помощники
Категория функций, которая помогает обрабатывать входные данные и выдавать их в нужном виде, например в байтах и т.д. А также те, которые позволяют работать с "фиктивными" адресами пользователей для ваших тестов.
6. Остальные
Есть также редко используемая категория, которая помогает работать с файлами и rpc ссылками, а также проводить более сложные тесты, типа фаззинга и инвариантов.
Работа с "фиктивными" адресами пользователей
В этом посте мы затронем тему, как писать тесты с использованием подставных адресов.
Для большего понимая объясню, что я имею ввиду под "фиктивными адресами". Это такие адреса, которые создаются только на время проведения теста. Их нельзя использовать больше нигде. Только в ваших тестах, как например, для проверки доступа к функциям.
Для этих целей используется специальный читкод - vm.addr(uint256);
Часто в больших проектах я встречал некий условный паттерн создания и работы с подобными адресами в контрактах, которым я хочу поделиться.
Итак, продолжим работать с нашим контрактом Counter и введем концепцию владельца, добавив в конструктор owner = msg.sender, и создав соответствующую переменную. Более того, для простых тестов добавим еще модификатор и простую функцию:
А теперь самое интересное, переходим в наш файл для тестов Counter.t.sol и создаем адреса пользователей.
Название переменных для ключевых ролей лучше писать заглавными буквами, как константы. В скобках vm.addr() вы можете написать вообще любое число uint256, а Foundry создаст на его основе рабочий уникальный адрес.
Также, в современных тестах используются специальный лейблы, для более понятного отображения адресов при дебаггинге, т.е. вместо адреса там будет стоять имя переменной.
Добавить лейбл к адресу очень просто:
P.S. Чаще всего видел, что лейблы помещали в setUp() функцию.
Теперь еще один интересный момент.
Когда мы в setUp() создаем новый контракт Counter и помещаем его в переменную, то владельцем становится адрес контракта нашего теста - CounterTest. Для "подмены" адреса вызывающего можно использовать еще несколько читкодов - vm.prank(), vm.startPrank() и vm.stopPrank().
vm.prank() - работает только на вызов, который идет после него, а vm.startPrank() - работает до тех пор, пока его не остановит vm.stopPrank().
В прошлом посте мы впервые встретились с читкодами, помните vm.expectRevert() и vm.expectEmit()?
Читкоды Foundry это специальные служебные функции в программе, которые помогают нам писать качественные тесты и манипулировать состоянием локального блокчейна! Вообще это очень крутая штука.
В течение цикла постов мы будем много раз встречать с ними, но для начала стоит разбить их по нескольким категориям использования.
1. Манипуляции локальным блокчейном
С помощью этой категории вы можете управлять состоянием блокчейна для своих тестов. Например, вы можете "перемотать время" или перескочить некоторое количество блоков, пополнить свои счета токенов и nft, установить block.difficulty или block.basefee, управлять nonce и многое другое.
2. Assertions
Другая категория, которая позволяет проводить сравнения ожидаемых результатов действия функции с полученными, примером подобных функций могут служить те же vm.expectRevert() и vm.expectEmit().
3. Форк сетей
Категория функций для работы с форками реальных сетей блокчейна для ваших тестов. Бывает крайне полезно оценить работу контракта на всех сетях, куда планируется загрузка!
4. Работа с переменными среды
Вы можете получать значения и обрабатывать данные из переменных среды, которые помогают управлять разработкой проекта.
5. Функции помощники
Категория функций, которая помогает обрабатывать входные данные и выдавать их в нужном виде, например в байтах и т.д. А также те, которые позволяют работать с "фиктивными" адресами пользователей для ваших тестов.
6. Остальные
Есть также редко используемая категория, которая помогает работать с файлами и rpc ссылками, а также проводить более сложные тесты, типа фаззинга и инвариантов.
Работа с "фиктивными" адресами пользователей
В этом посте мы затронем тему, как писать тесты с использованием подставных адресов.
Для большего понимая объясню, что я имею ввиду под "фиктивными адресами". Это такие адреса, которые создаются только на время проведения теста. Их нельзя использовать больше нигде. Только в ваших тестах, как например, для проверки доступа к функциям.
Для этих целей используется специальный читкод - vm.addr(uint256);
Часто в больших проектах я встречал некий условный паттерн создания и работы с подобными адресами в контрактах, которым я хочу поделиться.
Итак, продолжим работать с нашим контрактом Counter и введем концепцию владельца, добавив в конструктор owner = msg.sender, и создав соответствующую переменную. Более того, для простых тестов добавим еще модификатор и простую функцию:
address public owner;
constructor() {
owner = msg.sender;
}
modifier onlyOwner () {
require(msg.sender == owner, "Now an owner!");
_;
}
function setNumberOwner(uint256 newNumber) public onlyOwner{
number = newNumber;
}
А теперь самое интересное, переходим в наш файл для тестов Counter.t.sol и создаем адреса пользователей.
address ADMIN = vm.addr(23432432);
address HACKER = vm.addr(223343212432);
Название переменных для ключевых ролей лучше писать заглавными буквами, как константы. В скобках vm.addr() вы можете написать вообще любое число uint256, а Foundry создаст на его основе рабочий уникальный адрес.
Также, в современных тестах используются специальный лейблы, для более понятного отображения адресов при дебаггинге, т.е. вместо адреса там будет стоять имя переменной.
Добавить лейбл к адресу очень просто:
vm.label(ADMIN, "ADMIN");
vm.label(HACKER, "HACKER");
P.S. Чаще всего видел, что лейблы помещали в setUp() функцию.
Теперь еще один интересный момент.
Когда мы в setUp() создаем новый контракт Counter и помещаем его в переменную, то владельцем становится адрес контракта нашего теста - CounterTest. Для "подмены" адреса вызывающего можно использовать еще несколько читкодов - vm.prank(), vm.startPrank() и vm.stopPrank().
vm.prank() - работает только на вызов, который идет после него, а vm.startPrank() - работает до тех пор, пока его не остановит vm.stopPrank().
👍1🔥1👏1
Другими словами, если вы хотите, чтобы только последующий вызов был совершен из-под определенного адреса - используйте простой vm.prank(), если же требуется череда вызовов из-под адреса, то лучше использовать два последних читкода.
Итак, в функцию setUp(), перед созданием контракта Counter, помещаем следующую строку:
vm.prank(ADMIN);
И наш контракт Counter теперь будет иметь владельцем адрес ADMIN. Проверим это в тестах. Напишем простой тест:
Здесь мы определяем в тесте, что последующие вызовы будут идти от имени адреса админа и делаем вызов на изменение числа.
Если вместо ADMIN мы подставим адрес HACKER, то тест провалится, что обозначает правильную работу доступа к функции.
Кстати, вот небольшой лайфхак по работе со startPrank и stopPrank, который я подсмотрел в одном из конкурсных протоколов.
Обычно мы пишем тест и уже перед тем, как вызвать необходимую функции из под нужного аккаунта (владельца, пользователя, хакера), мы пишем:
vm.startPrank(user);
и в завершении:
vm.stopPrank();
Порой для тестов это приходится прописывать довольно часто. Так вот, в этом контракте придумали поместить это все в модификатор:
А потом использовать его, как обычно, в функциях:
На мой взгляд достаточно элегантное решение.
Завтра мы поговорим о простой организации файлов в папке тестов, а сейчас вы можете скачать файлы контракта и тестов к нему, чтобы попрактиковаться самим.
#foundry #lesson14
Итак, в функцию setUp(), перед созданием контракта Counter, помещаем следующую строку:
vm.prank(ADMIN);
И наш контракт Counter теперь будет иметь владельцем адрес ADMIN. Проверим это в тестах. Напишем простой тест:
function test_SetNumberOwner () public {
vm.startPrank(ADMIN);
counter.setNumberOwner(45);
vm.stopPrank();
}Здесь мы определяем в тесте, что последующие вызовы будут идти от имени адреса админа и делаем вызов на изменение числа.
Если вместо ADMIN мы подставим адрес HACKER, то тест провалится, что обозначает правильную работу доступа к функции.
Кстати, вот небольшой лайфхак по работе со startPrank и stopPrank, который я подсмотрел в одном из конкурсных протоколов.
Обычно мы пишем тест и уже перед тем, как вызвать необходимую функции из под нужного аккаунта (владельца, пользователя, хакера), мы пишем:
vm.startPrank(user);
и в завершении:
vm.stopPrank();
Порой для тестов это приходится прописывать довольно часто. Так вот, в этом контракте придумали поместить это все в модификатор:
modifier prank(address from) {
vm.startPrank(from);
_;
vm.stopPrank();
}А потом использовать его, как обычно, в функциях:
function test_addLiquidity() public prank(user) {}На мой взгляд достаточно элегантное решение.
Завтра мы поговорим о простой организации файлов в папке тестов, а сейчас вы можете скачать файлы контракта и тестов к нему, чтобы попрактиковаться самим.
#foundry #lesson14
❤1👍1🔥1
Foundry с 0. Часть 15
В завершении этой недели практики с Foundry стоит поговорить об организации файлов при написании тестов. Вы ведь сами понимаете, что "пихать" все тесты в один файл это не только глупо, но и трудночитаемо.
Попробуем представить, какие тесты нам могут потребоваться для нашего проекта:
1. Простые тесты, которые проверят работу функций;
2. Фаззинг тесты;
3. Инвариант тесты;
4. Форк тесты;
5. Интеграционные тесты, когда мы проверяем, что наш протокол правильно взаимодействует со сторонними контрактами, типа Uniswap или Chainlink;
Более того, нам, возможно, нужно будет проверить работу протокола с токенами и другими шаблонами, ну, и не стоит забывать про какие-нибудь хелперы или библиотеки, которые мы можем написать исключительно для наших тестов.
В целом, наша папка test может получиться такой:
В mocks хранятся шаблоны токенов и других контрактов, которые мы хотим развернуть для тестов, в scenario - сценарии логических тестов, в unit - тесты для условий и порождения событий, в utils - хелперы и библиотеки.
Это самый полный список папок и тестов, который мне удалось найти и собрать вместе. Также не стоит забывать, что было бы хорошо создать подробный readme.md файл или другую документацию с описанием тестов для последующей работы разработчиков и аудиторов.
Я бы также рекомендовал бы добавить сюда отдельную папку hacks, в которую помещать все poc тесты после аудита.
Как вы можете заметить, написание тестов занимает практически столько же времени, сколько и написание самих контрактов, если не больше. Это очень кропотливая работа, на грани с полноценным аудитом.
Надеюсь, этот пост поможет вам лучше составлять архитектуру своих тестов и проверять свой проект вдоль и поперек.
#foundry #lesson15
В завершении этой недели практики с Foundry стоит поговорить об организации файлов при написании тестов. Вы ведь сами понимаете, что "пихать" все тесты в один файл это не только глупо, но и трудночитаемо.
Попробуем представить, какие тесты нам могут потребоваться для нашего проекта:
1. Простые тесты, которые проверят работу функций;
2. Фаззинг тесты;
3. Инвариант тесты;
4. Форк тесты;
5. Интеграционные тесты, когда мы проверяем, что наш протокол правильно взаимодействует со сторонними контрактами, типа Uniswap или Chainlink;
Более того, нам, возможно, нужно будет проверить работу протокола с токенами и другими шаблонами, ну, и не стоит забывать про какие-нибудь хелперы или библиотеки, которые мы можем написать исключительно для наших тестов.
В целом, наша папка test может получиться такой:
test
|-- differential
|-- fork
|-- fuzzing
|-- integartion
|-- invariant
|-- mocks
|-- scenario
|-- unit
|-- utils
|- simple tests
В mocks хранятся шаблоны токенов и других контрактов, которые мы хотим развернуть для тестов, в scenario - сценарии логических тестов, в unit - тесты для условий и порождения событий, в utils - хелперы и библиотеки.
Это самый полный список папок и тестов, который мне удалось найти и собрать вместе. Также не стоит забывать, что было бы хорошо создать подробный readme.md файл или другую документацию с описанием тестов для последующей работы разработчиков и аудиторов.
Я бы также рекомендовал бы добавить сюда отдельную папку hacks, в которую помещать все poc тесты после аудита.
Как вы можете заметить, написание тестов занимает практически столько же времени, сколько и написание самих контрактов, если не больше. Это очень кропотливая работа, на грани с полноценным аудитом.
Надеюсь, этот пост поможет вам лучше составлять архитектуру своих тестов и проверять свой проект вдоль и поперек.
#foundry #lesson15
👍1🔥1
Foundry c 0. Содержание 1
Прошло уже три недели с момента, как мы начали разбирать тестирование с Foundry. Пришло время собрать материал в один пост для более удобной навигации.
Общий план цикла постов
Итак, за этом время мы прошли:
Введение
Foundry с 0. Часть 0. Установка
Foundry с 0. Часть 1. Из чего состоит Foundry?
Foundry с 0. Часть 2. Cast команды (1)
Foundry с 0. Часть 3. Cast команды (2)
Foundry с 0. Часть 4. Cast команды (3)
Foundry с 0. Часть 5. Chisel
Foundry с 0. Часть 6. Конфигурация Foundry
Foundry с 0. Часть 7. Библиотеки
Foundry с 0. Часть 8. Старт проекта
Foundry с 0. Часть 9. Виды тестов
Foundry с 0. Часть 10. Ресурсы
Базовая практика
Foundry с 0. Часть 11. Простые тесты
Foundry с 0. Часть 12. Разные assert
Foundry с 0. Часть 13. Fail тесты
Foundry с 0. Часть 14. Читкоды и prank()
Foundry с 0. Часть 15. Организация файлов
Даже учитывая то, что по Foundry сейчас можно найти достаточно много информации, все равно бывает сложно перевести ее и написать понятным языком.
Буду рад, если поддержите лайком и сделаете репост.
#foundry #summary
Прошло уже три недели с момента, как мы начали разбирать тестирование с Foundry. Пришло время собрать материал в один пост для более удобной навигации.
Общий план цикла постов
Итак, за этом время мы прошли:
Введение
Foundry с 0. Часть 0. Установка
Foundry с 0. Часть 1. Из чего состоит Foundry?
Foundry с 0. Часть 2. Cast команды (1)
Foundry с 0. Часть 3. Cast команды (2)
Foundry с 0. Часть 4. Cast команды (3)
Foundry с 0. Часть 5. Chisel
Foundry с 0. Часть 6. Конфигурация Foundry
Foundry с 0. Часть 7. Библиотеки
Foundry с 0. Часть 8. Старт проекта
Foundry с 0. Часть 9. Виды тестов
Foundry с 0. Часть 10. Ресурсы
Базовая практика
Foundry с 0. Часть 11. Простые тесты
Foundry с 0. Часть 12. Разные assert
Foundry с 0. Часть 13. Fail тесты
Foundry с 0. Часть 14. Читкоды и prank()
Foundry с 0. Часть 15. Организация файлов
Даже учитывая то, что по Foundry сейчас можно найти достаточно много информации, все равно бывает сложно перевести ее и написать понятным языком.
Буду рад, если поддержите лайком и сделаете репост.
#foundry #summary
👍23🔥4❤1
Foundry с 0. Часть 16
Думал тему из этого поста постепенно поднимать в нескольких других, но понял, что лучше основную идею изложить сейчас, а потом уже дополнять при необходимости.
А поговорим мы о работе с консолью: вызовом тестов и логированием из них.
Из предыдущих постов мы узнали, для того чтобы вызвать процесс тестирования нашего контракта, достаточно в консоли прописать:
или
и других "-v..." для более подробной информации.
Тем не менее, порой, когда тестовых контрактов и самих тестов очень много, нам нужно вызвать что-то конкретное, и ждать выполнения всех тестов из всех контрактов может стоит нам времени.
Для этого существуют удобные дополнения к команде test:
1. forge test --match-test testName
Выполним конкретный тест из контрактов.
2. forge test --match-contract contractName
Выполним все тесты из конкретного контракта.
3. forge test --match-path Path
Выполнить тесты по указанному пути.
Так же мы может исключить какой-нибудь контракт или тест из процесса тестирования с помощью опций:
--no-match-test
--no-match-contract
--no-match-path
Более того, с помощью таких опций-хелперов мы можем подключать optimizer и указывать количество проходов:
--optimize
--optimizer-runs
--via-ir
Или указать EVM версию для наших тестов:
--evm-version
Как вы, надеюсь, поняли, перед опцией должно быть forge test, а после указание на файл / тест / версию и так далее.
Но, что самое интересное, эти команды можно комбинировать для точечных тестов, например:
Также есть еще другие опции для тестов на форках, отслеживание газа и дебаггинг, но о них мы будем говорить уже в своих постах.
Все опции для тестов можно посмотреть тут:
https://book.getfoundry.sh/reference/forge/forge-test
Теперь пара слов о логировании в тестах.
Порой нам нужно будет выводить значения из наших тестов, чтобы узнать, кто был вызывающим функции, изменение баланса или результат деления. Для этого мы можем просто написать:
Однако, если таких вызовов много в ходе выполнения теста, то можно очень просто потеряться в них. Поэтому предлагаю вам пару способов для удобного вывода информации.
1. Подписывать логи:
Будет так:
Logs:
This is owner: 0x00c7bF7d9E7D071Df3B53dEec96cD4bf0f6c0220
2. Разделять по строкам:
Будет так:
Logs:
This is owner
0x00c7bF7d9E7D071Df3B53dEec96cD4bf0f6c0220
3. Выделять дефисами:
Будет так:
Logs:
This is owner ---------- 0x00c7bF7d9E7D071Df3B53dEec96cD4bf0f6c0220
4. Оставлять пробелы, используя пустые логи:
console.log(" ");
5. Выделять разным цветом.
Для этого потребуется в импорте тестов добавить библиотеку StdStyle:
import {Test, console, StdStyle} from "forge-std/Test.sol";
После этого можно применять цвета к логам:
Стилей достаточно много: красный, зеленый, желтый, синий, мажента, голубой, жирный, приглушенный, курсив, зачеркнутый и обычный.
Попробуйте сами, так наглядность логов в ваших тестах станет еще лучше!
#foundry #lesson16
Думал тему из этого поста постепенно поднимать в нескольких других, но понял, что лучше основную идею изложить сейчас, а потом уже дополнять при необходимости.
А поговорим мы о работе с консолью: вызовом тестов и логированием из них.
Из предыдущих постов мы узнали, для того чтобы вызвать процесс тестирования нашего контракта, достаточно в консоли прописать:
forge test
или
forge test -vv
и других "-v..." для более подробной информации.
Тем не менее, порой, когда тестовых контрактов и самих тестов очень много, нам нужно вызвать что-то конкретное, и ждать выполнения всех тестов из всех контрактов может стоит нам времени.
Для этого существуют удобные дополнения к команде test:
1. forge test --match-test testName
Выполним конкретный тест из контрактов.
2. forge test --match-contract contractName
Выполним все тесты из конкретного контракта.
3. forge test --match-path Path
Выполнить тесты по указанному пути.
Так же мы может исключить какой-нибудь контракт или тест из процесса тестирования с помощью опций:
--no-match-test
--no-match-contract
--no-match-path
Более того, с помощью таких опций-хелперов мы можем подключать optimizer и указывать количество проходов:
--optimize
--optimizer-runs
--via-ir
Или указать EVM версию для наших тестов:
--evm-version
Как вы, надеюсь, поняли, перед опцией должно быть forge test, а после указание на файл / тест / версию и так далее.
Но, что самое интересное, эти команды можно комбинировать для точечных тестов, например:
forge test --match-contract myContract --match-test myTest --evm-version london
Также есть еще другие опции для тестов на форках, отслеживание газа и дебаггинг, но о них мы будем говорить уже в своих постах.
Все опции для тестов можно посмотреть тут:
https://book.getfoundry.sh/reference/forge/forge-test
Теперь пара слов о логировании в тестах.
Порой нам нужно будет выводить значения из наших тестов, чтобы узнать, кто был вызывающим функции, изменение баланса или результат деления. Для этого мы можем просто написать:
console.log(param);
Однако, если таких вызовов много в ходе выполнения теста, то можно очень просто потеряться в них. Поэтому предлагаю вам пару способов для удобного вывода информации.
1. Подписывать логи:
console.log("This is owner: ", contract.owner());Будет так:
Logs:
This is owner: 0x00c7bF7d9E7D071Df3B53dEec96cD4bf0f6c0220
2. Разделять по строкам:
console.log("This is owner");
console.log(contract.owner());Будет так:
Logs:
This is owner
0x00c7bF7d9E7D071Df3B53dEec96cD4bf0f6c0220
3. Выделять дефисами:
console.log("This is owner ----------- ", contract.owner());Будет так:
Logs:
This is owner ---------- 0x00c7bF7d9E7D071Df3B53dEec96cD4bf0f6c0220
4. Оставлять пробелы, используя пустые логи:
console.log(" ");
5. Выделять разным цветом.
Для этого потребуется в импорте тестов добавить библиотеку StdStyle:
import {Test, console, StdStyle} from "forge-std/Test.sol";
После этого можно применять цвета к логам:
console.log("Owner: ", StdStyle.yellow(counter.owner()));Стилей достаточно много: красный, зеленый, желтый, синий, мажента, голубой, жирный, приглушенный, курсив, зачеркнутый и обычный.
Попробуйте сами, так наглядность логов в ваших тестах станет еще лучше!
#foundry #lesson16
❤7👍1