javascript

Web3 — Разрабатываем магазин без базы данных

  • суббота, 31 января 2026 г. в 00:00:04
https://habr.com/ru/articles/984698/

Для начала определимся, что мы хотим получить.

  1. Авторизация с кошельком через metamask extension.

  2. Магазин внутри-игровых предметов. При покупке, осуществляется перевод средств на адрес магазина и записывается адрес покупателя и id предмета в контракт. Можно запросить купленные предметы.

Запрос транзакции
Запрос транзакции
Завершение транзакции
Завершение транзакции

Фреймворк для разработки и тестирования

Во-первых, понадобятся инструменты для сборки, тестирования, отладки и публикации смарт-контрактов. Современные решения: foundry и hardhat3. Я использовал foundry.

Для разработки также потребуется тестнет. Можно использовать стороннее решение, или воспользоваться тестнетом представляемым фреймворком. Я использовал anvil из foundry.

anvil запускается и работает из консоли. Функционал стандартный, тестнет создает аккаунты со средствами на счетах и предоставляет RPC точку(вида, http://127.0.0.1:8545) для работы. По этому адресу нужно подключить metamask extension. А также туда публикуется собранный контракт.

Тестнет anvil
Тестнет anvil
Подключение тестнета в metamask extension
Подключение тестнета в metamask extension

Сборка и публикация

Контракт будет написан на solidity. Нам понадобятся две функции:

1. Покупка предмета. Покупка будет передавать средства на кошелек магазина и записывать покупателю id купленного предмета.

function purchaseItem(uint8 _itemId) external payable {    
  uint256 itemCost = 2 gwei; // 0.0012 USD / 0.18 RUB;    
  // Forward the funds to the seller
  (bool success, ) = STORE_ADDRESS.call{value: itemCost}("");    
  require(success, "Transfer failed");    
  boughtItems[msg.sender].push(_itemId);
}

2. Просмотр купленных предметов.

function getBoughtItems() external view returns (uint8[] memory) {    
  return boughtItems[msg.sender];
}

Также запишем адрес кошелька магазина при инициализации контракта.

constructor(address _store) {    
  STORE_ADDRESS = _store;
}

Для сборки, тестирования и публикации я использовал forge из фреймворка foundry. Тесты для дураков приводить здесь не буду. После успешного тестирования и сборки, публикуем контракт:

forge create ./src/Store.sol:Store \    
--private-key <PRIVATE_KEY> \    
--rpc-url <RPC_URL> \    
--broadcast \    
--constructor-args <STORE_PUBLIC_KEY>

После публикации, нам предоставляют адрес смарт-контракта. Строка "Deployed to". Его будем использовать в следующей части при подключении интерфейса.

Результат успешной публикации смарт-контракта
Результат успешной публикации смарт-контракта

Интерфейс магазина

Понадобится библиотека для взаимодействия с metamask и блокчейном. Я использовал ethers v6, она показалась мне наиболее хорошо документированной.

Подключаемся к кошельку через metamask extension.

import { ethers } from "ethers";
const provider = new ethers.BrowserProvider(window.ethereum); 

Запросим баланс, чтобы убедится в корректной работе.

const signerObj = await provider.getSigner();
const balance = await provider.getBalance(signerObj.address);        
const balanceFormatted = ethers.formatEther(balance);

Взаимодействие со смарт-контрактом

1. Нам нужен abi собранного контракта. Это строка с описанием методов смарт-контракта, его можно написать самостоятельно или взять в артефакте после сборки.

import ContractArtifact from "***/Store.json";
const abi = ContractArtifact.abi;

2. Создаем экземпляр Contract, передаем туда адрес смарт-контракта, который выдали после сборки, abi и объект полученный при авторизации.

const contract = new ethers.Contract(CONTRACT_ADDRESS, abi, signerObj);

3. Теперь можно вызывать функции смарт-контракта.

// покупка предмета
contract.purchaseItem(itemId, {    
  value: ethers.parseUnits("2", "gwei")
});
// получение списка покупок
const userItems = await contract.getBoughtItems();

По завершении работ, можно залить магазин и контракт в настоящую сеть. Для публикации потребуется небольшое количество эфира.

Ссылки
И злополучная книга

Всем спасибо за внимание!