Blockchain и Web3: децентрализованные приложения
Smart contracts, DeFi, NFT и разработка dApps на Ethereum
Web3 обещает вернуть контроль над данными пользователям. Blockchain — не только криптовалюты, это новая парадигма для приложений. Разберем как разрабатывать децентрализованные приложения. Что такое Web3 Эволюция Web: Web1 (1990-2004) — статические страницы, read-only Web2 (2004-2020) — интерактивные приложения, социальные сети Web3 (2020+) — децентрализация, ownership, trustless Ключевые принципы Web3: Decentralization — нет центрального контроля Ownership — пользователи владеют данными Trustless — не нужно доверять посредникам Permissionless — доступ для всех Blockchain основы Что такое блокчейн: Распределенная база данных с криптографической защитой, где каждый блок содержит: Данные транзакций Timestamp Hash предыдущего блока Nonce (для proof-of-work) Простой блокчейн на JavaScript: const crypto = require('crypto'); class Block { constructor(timestamp, data, previousHash = '') { this.timestamp = timestamp; this.data = data; this.previousHash = previousHash; this.hash = this.calculateHash(); this.nonce = 0; } calculateHash() { return crypto .createHash('sha256') .update( this.previousHash + this.timestamp + JSON.stringify(this.data) + this.nonce ) .digest('hex'); } mineBlock(difficulty) { while (this.hash.substring(0, difficulty) !== Array(difficulty + 1).join('0')) { this.nonce++; this.hash = this.calculateHash(); } console.log('Block mined:', this.hash); } } class Blockchain { constructor() { this.chain = [this.createGenesisBlock()]; this.difficulty = 2; } createGenesisBlock() { return new Block(Date.now(), 'Genesis Block', '0'); } getLatestBlock() { return this.chain[this.chain.length - 1]; } addBlock(newBlock) { newBlock.previousHash = this.getLatestBlock().hash; newBlock.mineBlock(this.difficulty); this.chain.push(newBlock); } isChainValid() { for (let i = 1; i Ethereum и Smart Contracts Что такое Smart Contract: Самоисполняющийся код на блокчейне. Условия автоматически выполняются без посредников. Простой контракт на Solidity: // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract SimpleStorage { uint256 private value; event ValueChanged(uint256 newValue); function setValue(uint256 newValue) public { value = newValue; emit ValueChanged(newValue); } function getValue() public view returns (uint256) { return value; } } ERC-20 Token (стандарт токенов): // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract MyToken { string public name = "My Token"; string public symbol = "MTK"; uint8 public decimals = 18; uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); constructor(uint256 initialSupply) { totalSupply = initialSupply * 10 ** decimals; balanceOf[msg.sender] = totalSupply; } function transfer(address to, uint256 value) public returns (bool) { require(balanceOf[msg.sender] >= value, "Insufficient balance"); balanceOf[msg.sender] -= value; balanceOf[to] += value; emit Transfer(msg.sender, to, value); return true; } function approve(address spender, uint256 value) public returns (bool) { allowance[msg.sender][spender] = value; emit Approval(msg.sender, spender, value); return true; } function transferFrom(address from, address to, uint256 value) public returns (bool) { require(balanceOf[from] >= value, "Insufficient balance"); require(allowance[from][msg.sender] >= value, "Allowance exceeded"); balanceOf[from] -= value; balanceOf[to] += value; allowance[from][msg.sender] -= value; emit Transfer(from, to, value); return true; } } Web3.js: взаимодействие с блокчейном Подключение к Ethereum: import Web3 from 'web3'; // Подключение к MetaMask const web3 = new Web3(window.ethereum); // Или к Infura const web3 = new Web3('https://mainnet.infura.io/v3/YOUR_PROJECT_ID'); // Запросить доступ к аккаунтам await window.ethereum.request({ method: 'eth_requestAccounts' }); const accounts = await web3.eth.getAccounts(); const balance = await web3.eth.getBalance(accounts[0]); console.log('Balance:', web3.utils.fromWei(balance, 'ether'), 'ETH'); Взаимодействие со Smart Contract: const contractABI = [ /* ABI from Remix or Hardhat */ ]; const contractAddress = '0x...'; const contract = new web3.eth.Contract(contractABI, contractAddress); // Read (не требует gas) const value = await contract.methods.getValue().call(); // Write (требует gas) const accounts = await web3.eth.getAccounts(); await contract.methods.setValue(42).send({ from: accounts[0], gas: 100000 }); // Слушаем события contract.events.ValueChanged() .on('data', (event) => { console.log('New value:', event.returnValues.newValue); }) .on('error', console.error); Ethers.js: современная альтернатива import { ethers } from 'ethers'; // Подключение к провайдеру const provider = new ethers.providers.Web3Provider(window.ethereum); const signer = provider.getSigner(); // Получение баланса const address = await signer.getAddress(); const balance = await provider.getBalance(address); console.log(ethers.utils.formatEther(balance), 'ETH'); // Взаимодействие с контрактом const contract = new ethers.Contract(contractAddress, contractABI, signer); // Read const value = await contract.getValue(); // Write const tx = await contract.setValue(42); await tx.wait(); // Ждем подтверждения // События contract.on('ValueChanged', (newValue) => { console.log('Value changed to:', newValue.toString()); }); NFT: Non-Fungible Tokens ERC-721 стандарт: // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyNFT is ERC721, Ownable { uint256 private _tokenIds; mapping(uint256 => string) private _tokenURIs; constructor() ERC721("MyNFT", "MNFT") {} function mint(address to, string memory tokenURI) public onlyOwner returns (uint256) { _tokenIds++; uint256 newTokenId = _tokenIds; _mint(to, newTokenId); _tokenURIs[newTokenId] = tokenURI; return newTokenId; } function tokenURI(uint256 tokenId) public view override returns (string memory) { require(_exists(tokenId), "Token does not exist"); return _tokenURIs[tokenId]; } } Mint NFT из JavaScript: const contract = new ethers.Contract(nftAddress, nftABI, signer); // Metadata в JSON const metadata = { name: "My Awesome NFT #1", description: "A unique digital collectible", image: "ipfs://QmX...", attributes: [ { trait_type: "Rarity", value: "Legendary" }, { trait_type: "Power", value: 100 } ] }; // Upload metadata в IPFS const metadataURI = await uploadToIPFS(metadata); // Mint NFT const tx = await contract.mint(userAddress, metadataURI); const receipt = await tx.wait(); const tokenId = receipt.events[0].args.tokenId; console.log('NFT minted with ID:', tokenId.toString()); DeFi: Decentralized Finance Uniswap-like Swap: // Simplified DEX concept contract SimpleDEX { mapping(address => mapping(address => uint256)) public liquidity; function addLiquidity(address token, uint256 amount) public { IERC20(token).transferFrom(msg.sender, address(this), amount); liquidity[token][msg.sender] += amount; } function swap( address fromToken, address toToken, uint256 amount ) public { require(liquidity[toToken][address(this)] > 0, "No liquidity"); IERC20(fromToken).transferFrom(msg.sender, address(this), amount); uint256 outputAmount = calculateSwap(fromToken, toToken, amount); IERC20(toToken).transfer(msg.sender, outputAmount); } function calculateSwap( address fromToken, address toToken, uint256 amount ) public view returns (uint256) { // Simplified constant product formula: x * y = k // In reality, uses more complex AMM formulas return amount * 95 / 100; // 5% fee } } IPFS: хранение файлов import { create } from 'ipfs-http-client'; const ipfs = create({ url: 'https://ipfs.infura.io:5001' }); // Upload файла async function uploadToIPFS(file) { const result = await ipfs.add(file); return `ipfs://${result.path}`; } // Upload JSON async function uploadJSON(data) { const result = await ipfs.add(JSON.stringify(data)); return `ipfs://${result.path}`; } // Получение файла async function getFromIPFS(cid) { const stream = ipfs.cat(cid); const chunks = []; for await (const chunk of stream) { chunks.push(chunk); } return Buffer.concat(chunks).toString(); } Hardhat: разработка и тестирование hardhat.config.js: require("@nomicfoundation/hardhat-toolbox"); module.exports = { solidity: "0.8.19", networks: { hardhat: {}, sepolia: { url: process.env.SEPOLIA_URL, accounts: [process.env.PRIVATE_KEY] } } }; Тестирование: const { expect } = require("chai"); const { ethers } = require("hardhat"); describe("MyToken", function () { let token; let owner; let addr1; beforeEach(async function () { [owner, addr1] = await ethers.getSigners(); const Token = await ethers.getContractFactory("MyToken"); token = await Token.deploy(1000000); }); it("Should assign total supply to owner", async function () { const ownerBalance = await token.balanceOf(owner.address); expect(await token.totalSupply()).to.equal(ownerBalance); }); it("Should transfer tokens", async function () { await token.transfer(addr1.address, 50); expect(await token.balanceOf(addr1.address)).to.equal(50); }); }); Деплой: // scripts/deploy.js async function main() { const Token = await ethers.getContractFactory("MyToken"); const token = await Token.deploy(1000000); await token.deployed(); console.log("Token deployed to:", token.address); } main().catch((error) => { console.error(error); process.exit(1); }); // Run: npx hardhat run scripts/deploy.js --network sepolia Web3 React Integration import { useState, useEffect } from 'react'; import { ethers } from 'ethers'; function useWallet() { const [account, setAccount] = useState(null); const [provider, setProvider] = useState(null); const connect = async () => { if (!window.ethereum) { alert('Please install MetaMask'); return; } const provider = new ethers.providers.Web3Provider(window.ethereum); await provider.send("eth_requestAccounts", []); const signer = provider.getSigner(); const address = await signer.getAddress(); setProvider(provider); setAccount(address); }; useEffect(() => { if (window.ethereum) { window.ethereum.on('accountsChanged', (accounts) => { setAccount(accounts[0] || null); }); } }, []); return { account, provider, connect }; } function App() { const { account, provider, connect } = useWallet(); return ( {!account ? ( Connect Wallet ) : ( Connected: {account.slice(0, 6)}...{account.slice(-4)} )} ); } Security Best Practices Reentrancy attacks — используйте ReentrancyGuard Integer overflow — используйте SafeMath (или Solidity 0.8+) Access control — правильные modifiers Gas optimization — минимизируйте storage operations Audit — профессиональный аудит перед mainnet Gas Optimization // Плохо - дорого for (uint i = 0; i Будущее Web3 Layer 2 — Optimism, Arbitrum для масштабирования Zero-knowledge proofs — privacy + scalability Account abstraction — лучший UX DAO — децентрализованное управление Заключение: Web3 — новая эра интернета с децентрализацией и ownership. Начните с изучения Solidity и Ethereum. Практикуйтесь с Hardhat. Используйте Ethers.js для frontend интеграции. Помните о security — аудит обязателен. Web3 технологии быстро развиваются — следите за обновлениями. Будущее — за децентрализацией.