китаев.tech

Первый контракт — написать и задеплоить

Как написать простой Solidity-контракт, скомпилировать его в Hardhat и задеплоить в локальную Ethereum-сеть.

Что будем деплоить

Первый контракт должен быть маленьким: одно состояние, одна функция чтения, одна транзакция записи.

В этом примере контракт MessageBoard хранит строку message. Скрипт деплоит контракт в локальную сеть Hardhat, читает стартовое значение и меняет его транзакцией.


Создаём проект

bash
mkdir first-ethereum-contract
cd first-ethereum-contract

npm init -y
npm pkg set type="module"
npm install --save-dev hardhat typescript tsx @types/node @nomicfoundation/hardhat-toolbox-mocha-ethers

mkdir contracts scripts

Создайте файл hardhat.config.ts:

typescript
import { defineConfig } from "hardhat/config";
import hardhatToolboxMochaEthers from "@nomicfoundation/hardhat-toolbox-mocha-ethers";

export default defineConfig({
  plugins: [hardhatToolboxMochaEthers],
  solidity: {
    version: "0.8.28",
  },
});

Пишем контракт

Создайте файл contracts/MessageBoard.sol:

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract MessageBoard {
    string private message;

    event MessageChanged(address indexed author, string newMessage);

    constructor(string memory initialMessage) {
        message = initialMessage;
    }

    function readMessage() external view returns (string memory) {
        return message;
    }

    function changeMessage(string calldata newMessage) external {
        message = newMessage;
        emit MessageChanged(msg.sender, newMessage);
    }
}

Проверьте компиляцию:

bash
npx hardhat build

Контракт готов к деплою, если Hardhat создал артефакты без ошибок компилятора Solidity.


Деплоим контракт

Создайте файл scripts/deploy-message-board.ts:

typescript
import { network } from "hardhat";

const { ethers } = await network.create();

const messageBoard = await ethers.deployContract("MessageBoard", [
  "Первое сообщение из конструктора",
]);

await messageBoard.waitForDeployment();

const contractAddress = await messageBoard.getAddress();
const initialMessage = await messageBoard.readMessage();

console.log("Адрес MessageBoard:", contractAddress);
console.log("Стартовое сообщение:", initialMessage);

const tx = await messageBoard.changeMessage("Сообщение изменено транзакцией");
await tx.wait();

const updatedMessage = await messageBoard.readMessage();

console.log("Новое сообщение:", updatedMessage);

В первом терминале запустите локальную сеть:

bash
npx hardhat node

Во втором терминале выполните deploy-скрипт:

bash
npx hardhat run scripts/deploy-message-board.ts --network localhost

В выводе появится адрес контракта, стартовое сообщение и новое сообщение после транзакции.


Как это работает

constructor() — выполняется один раз при деплое и записывает первое сообщение в storage.

string private message — хранит строку в состоянии контракта. Это данные внутри Ethereum, а не переменная в JavaScript.

readMessage()view-функция. Она читает состояние и не тратит gas при локальном RPC-вызове.

changeMessage() — меняет состояние, поэтому Hardhat отправляет транзакцию.

event MessageChanged — пишет лог в receipt транзакции. Так frontend или backend могут отслеживать изменения.

ethers.deployContract("MessageBoard", [...]) — деплоит контракт и передаёт аргументы в constructor.

tx.wait() — ждёт, пока транзакция попадёт в блок локальной сети.


Что происходит при деплое

Деплой — это обычная Ethereum-транзакция без адреса получателя. Вместо вызова существующего контракта она отправляет bytecode нового контракта в сеть.

После включения транзакции в блок сеть создаёт новый адрес. По этому адресу теперь лежит код MessageBoard и его состояние.


Частые ошибки

Забыли аргументы конструктора → передайте массив аргументов вторым параметром в ethers.deployContract().

Hardhat не находит контракт → проверьте имя файла, имя контракта и результат npx hardhat build.

Транзакция отправлена, но значение не изменилось в выводе → убедитесь, что вы вызвали await tx.wait() перед повторным чтением.


Что дальше

Материалы китаev.tech публикуются в образовательных целях и не являются инвестиционной рекомендацией. Примеры кода и описания протоколов — для обучения; использование в продакшне на ваш собственный риск. Дисклеймер и политика конфиденциальности.