Я изучаю учебник Патрика Коллинза. Этот учебник представляет собой фантастическое введение в разработку блокчейна и web3 с использованием JavaScript, Solidity и Hardhat. Один из уроков был направлен на разработку и развертывание смарт-контракта лотереи (Raffle) с использованием hardhat-deploy, тестовой сети hardhat dev и тестовой сети Rinkeby. Патрик также реализует машинописную версию своего кода.
Эта статья призвана развить эту работу, обновив приложение до TypeScript и используя последние версии всех зависимостей. Я хотел бы использовать TypeScript сильнее. Мы также переключимся с тестовой сети Rinkeyby на более современную тестовую сеть Sepolia.
Это не полное руководство по созданию приложения в каске. Это просто расширение ультрасовременного руководства Патрика Коллинза на 2023 год, когда у нас будет версия 6 ehters.js.
Это репозиторий машинописной ветки Патрика Коллинза https://github.com/PatrickAlphaC/hardhat-smartcontract-lottery-fcc/tree/typescript
Мы внесем несколько небольших исправлений в соответствии с быстро меняющимися мирами JS и блокчейна.
Установка и подготовка
У Hardhat отличная документация https://hardhat.org/hardhat-runner/docs/getting-started#installation. Я опишу в двух словах о касках.
Установка и настройка
Вы можете установить каску глобально или локально. Я предпочитаю второй вариант, но если вы хотите стать суперквалифицированным старшим разработчиком смарт-контрактов, вам, вероятно, потребуется установить его глобально. В любом случае, мы должны как-то установить каску.
Тогда мы должны инициировать проект
hardhat npx hardhat
Или после установки каски-сокращения вы можете использовать просто
hh
Чтобы упростить процесс, мы выбрали вариант проекта Typescript:
Согласитесь со всем и настройте его.
Нам не нужны контракты, сценарии и тесты, созданные Hardhat.
rm -rf contract/ scripts/ test/
У нас будет тот же контракт Solidity, что и у Патрика. https://github.com/shaggyrec/hardhat-smartcontract-lottery-ts/blob/main/contracts/Raffle.sol
Здесь нет никаких изменений.
Мы также должны установить пару зависимостей
npm i @nomicfoundation/hardhat-toolbox @nomiclabs/hardhat-ethers@npm:hardhat-deploy-ethers dotenv hardhat-gas-reporter -D
Я обычно использую `eslint`. Патрик использовал слово «красивее». Неважно, какой инструмент вы выберете. Но было бы хорошей практикой выбрать что-то, потому что JS позволяет вам слишком много, и у вас должны быть границы, чтобы ваш код был в отличном состоянии.
Для `eslint` мне нужно сделать
npm i eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser -D
и включите его в IDE.
Мы можем заставить hh скомпилировать, чтобы иметь контрактные типизации для написания нашего кода. Но это не обязательно. Hardhat выполняет эту команду прямо перед развертыванием.
Конфигурация
Мы хотим использовать TypeScript. И мы хотим использовать это правильно. У Патрика Коллинза есть `helper-hardhat-config.ts` в его репозитории, мы не будем. Вместо него мы расширим конфиг Hardhat.
Я не хочу использовать какие-либо дополнительные файлы конфигурации. И я действительно хочу иметь все конфигурации текущей сети в опции «сеть». В этом разница между решением Патрика и текущим решением. В следующем конфигурационном файле вы можете увидеть опции `blockConfirmations`, `entranceFee`, `gasLane`, `subscriptionId`, `callbackGasLimit`, `interval` прямо в сетевом объекте.
import '@typechain/hardhat'; import '@nomicfoundation/hardhat-toolbox'; import '@nomiclabs/hardhat-ethers'; import '@nomicfoundation/hardhat-ethers'; import 'hardhat-deploy'; import 'hardhat-gas-reporter'; import './type-extensions'; import 'dotenv/config'; import { HardhatUserConfig } from 'hardhat/config'; import { ethers } from 'ethers'; const config: HardhatUserConfig = { solidity: '0.8.18', defaultNetwork: 'hardhat', networks: { hardhat: { chainId: 31337, blockConfirmations: 1, entranceFee: ethers.parseEther('0.01'), gasLane: '0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c', subscriptionId: '', callbackGasLimit: '500000', interval: 30 }, sepolia: { chainId: 11155111, url: process.env.SEPOLIA_RPC_URL, accounts: [process.env.SEPOLIA_PRIVATE_KEY as string], blockConfirmations: 6, vrfCoordinator: '0x8103B0A8A00be2DDC778e6e7eaa21791Cd364625', entranceFee: ethers.parseEther('0.01'), gasLane: '0x474e34a077df58807dbe9c96d3c009b23b3c6d0cce433e59bbf5b34f823bc56c', subscriptionId: '3092', callbackGasLimit: '500000', interval: 30 } }, etherscan: { apiKey: { sepolia: process.env.ETHERSCAN_API_KEY || '' }, }, namedAccounts: { deployer: { default: 0 }, player: { default: 1 } }, developmentChains: ['hardhat', 'localhost'], organizerFee: 10, mocha: { timeout: 40000 } }; export default config;
Но этого недостаточно. TypeScript/Eslint покажет ошибки
Еще одна важная вещь.
В ethers 6 вы больше не можете использовать `ethers.utils.parseEther(‘0.01’)`, функции не существует. `Utils` не выход. Мы должны использовать просто ethers.parseEther(‘0.01’)`.
Я создал файл `type-extensions.ts`, чтобы расширить конфигурацию по умолчанию.
import 'hardhat/types/config'; declare module 'hardhat/types/config' { export interface HttpNetworkUserConfig { blockConfirmations?: number; vrfCoordinator?: string; entranceFee: bigint; gasLane: string; subscriptionId?: string; callbackGasLimit: string; interval: number; } export interface HardhatNetworkUserConfig { blockConfirmations?: number; entranceFee: bigint; gasLane: string; subscriptionId?: string; callbackGasLimit?: string; interval: number; } export interface HardhatUserConfig { developmentChains: string[]; organizerFee: number; } export interface HttpNetworkConfig { blockConfirmations: number; vrfCoordinator: string; entranceFee: bigint; gasLane: string; subscriptionId: string; callbackGasLimit: string; interval: number; } export interface HardhatNetworkConfig { blockConfirmations: number; vrfCoordinator?: string; entranceFee: bigint; gasLane: string; subscriptionId: string; callbackGasLimit: string; interval: number; } export interface HardhatConfig { developmentChains: string[]; organizerFee: number; } }
Я считаю, что структура очень ясна. Объявления с суффиксом `UserConfig` предназначены для конфигурации каски. Объявления с суффиксом `Config` предназначены для демонстрации этих свойств в ваших скриптах.
Дай посмотреть. Файл `deploy/01-deploy-raffle.ts` должен быть похож на
import { HardhatRuntimeEnvironment } from 'hardhat/types'; import { config, network } from 'hardhat'; import { DeployFunction } from 'hardhat-deploy/dist/types'; import verify from '../utils/verify'; import { VRFCoordinatorV2Mock } from '../typechain-types'; import { EventLog } from 'ethers'; const deployRaffle: DeployFunction = async function ({ getNamedAccounts, deployments, ethers }: HardhatRuntimeEnvironment) { const { deploy, log } = deployments; const { deployer } = await getNamedAccounts(); let vrfCoordinatorV2Address = network.config.vrfCoordinator; let subscriptionId = network.config.subscriptionId; let vrfCoordinatorV2Mock: VRFCoordinatorV2Mock; if (config.developmentChains.includes(network.name)) { vrfCoordinatorV2Mock = await ethers.getContract('VRFCoordinatorV2Mock'); vrfCoordinatorV2Address = await vrfCoordinatorV2Mock.getAddress(); const transactionReceipt = await (await vrfCoordinatorV2Mock.createSubscription()) .wait(1); subscriptionId = (transactionReceipt?.logs[0] as EventLog).args.subId; await vrfCoordinatorV2Mock.fundSubscription(subscriptionId, ethers.parseEther('2')); } const args = [ vrfCoordinatorV2Address, network.config.entranceFee, network.config.gasLane, subscriptionId, network.config.callbackGasLimit, network.config.interval, config.organizerFee ]; const raffle = await deploy('Raffle', { from: deployer, args, log: true, waitConfirmations: network.config.blockConfirmations || 1 }); if (config.developmentChains.includes(network.name)) { await vrfCoordinatorV2Mock!.addConsumer(subscriptionId, raffle.address); } if (!config.developmentChains.includes(network.name) && process.env.ETHERSCAN_API_KEY) { await verify(raffle.address, args, log); } log('----------------------------'); }; deployRaffle.tags = ['all', 'raffle']; export default deployRaffle;
Мы импортировали «сеть» из «каски», и я могу использовать «network.config.vrfCoordinator», «network.config.subscriptionId». Потрясающий!
Еще одна замечательная вещь заключается в том, что у нас есть подсказки для нашего объекта контракта.
Мы можем получить контракт, используя дженерики TypeSctipt.
import { Raffle } from '../typechain-types'; const raffle = await ethers.getContract<Raffle>('Raffle');
И теперь мы можем использовать такую магию:
Все остальное в приложении то же самое. Ссылку на репозиторий я прикреплю в конце этой статьи.
Тесты
Тесты почти одинаковые. У нас есть разница в случае «больших» взаимодействий. Но это так просто:
const contractBalance = BigInt(raffleEntranceFee) * BigInt(additionalEntrances) + BigInt(raffleEntranceFee);
Кроме того, мы должны использовать вместо
raffle.callStatic.checkUpkeep("0x")
немного другой
raffle.checkUpkeep.staticCall('0x');
И мы можем заменить этот громоздкий функционал машины времени
await network.provider.send('evm_increaseTime', [interval + 1]); await network.provider.request({ method: 'evm_mine', params: [] });
с лаконичным
import { time } from '@nomicfoundation/hardhat-toolbox/network-helpers'; … await time.increase(interval + 1);
У меня есть два пропущенных теста, потому что на момент написания этой статьи срабатывание событий не работало должным образом в последней версии. Ссылка на выпуск GitHub https://github.com/NomicFoundation/hardhat/issues/4098
Сеполия
Патрик Коллинз работает с Ринкеби в своем руководстве. Использование Sepolia очень похоже.
Создание подписки https://docs.chain.link/vrf/v2/subscription/supported-networks/#sepolia-testnet
ВРФ вещи https://vrf.chain.link
Поддерживать регистрацию https://automation.chain.link/sepolia
Эфирискан https://sepolia.etherscan.io/
Заключение
Можем развернуть и войти в лотерею
hh deploy - network sepolia hh run scripts/enterRaffle.ts - network sepolia
Мы использовали последнюю версию для всего и немного переписали приложение Freecodecamp Патрика Коллинза Raffle.
У нас проблема с подпиской на события в последней версии Hardhat. Но я верю, что это будет исправлено в ближайшее время.
Вот он, лотерейный смарт-контракт, созданный с помощью Hardhat, TypeScript и последних версий всех зависимостей и развернутый локально или в тестовой сети Sepolia.
Ссылка на репозиторий этой статьи — https://github.com/shaggyrec/hardhat-smartcontract-lottery-ts.
Подписывайтесь на меня