diff --git a/backend/contracts/MyContract.sol b/backend/contracts/MyContract.sol index cf1fec5..e50c519 100644 --- a/backend/contracts/MyContract.sol +++ b/backend/contracts/MyContract.sol @@ -1,27 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -contract MyContract { - address public owner; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +contract MyContract is Ownable, ReentrancyGuard { uint256 public price; event Purchase(address buyer, uint256 amount); constructor() { - owner = msg.sender; price = 0.01 ether; // Начальная цена 0.01 ETH } - modifier onlyOwner() { - require(msg.sender == owner, "Only owner can call this function"); - _; - } - - function setOwner(address newOwner) public onlyOwner { - require(newOwner != address(0), "New owner cannot be zero address"); - owner = newOwner; - } - function setPrice(uint256 newPrice) public onlyOwner { price = newPrice; } @@ -30,12 +21,13 @@ contract MyContract { return price; } - function purchase(uint256 amount) public payable { + function purchase(uint256 amount) public payable nonReentrant { require(msg.value == price * amount, "Incorrect payment amount"); emit Purchase(msg.sender, amount); } - function withdraw() public onlyOwner { - payable(owner).transfer(address(this).balance); + function withdraw() public onlyOwner nonReentrant { + (bool success, ) = owner().call{value: address(this).balance}(""); + require(success, "Transfer failed"); } } \ No newline at end of file diff --git a/backend/hardhat.config.cjs b/backend/hardhat.config.cjs deleted file mode 100644 index cd28b26..0000000 --- a/backend/hardhat.config.cjs +++ /dev/null @@ -1,12 +0,0 @@ -require("@nomiclabs/hardhat-waffle"); -require("dotenv").config(); - -module.exports = { - solidity: "0.8.0", - networks: { - sepolia: { - url: process.env.ETHEREUM_NETWORK_URL, - accounts: [process.env.PRIVATE_KEY] - } - } -}; \ No newline at end of file diff --git a/backend/hardhat.config.js b/backend/hardhat.config.js index a875842..79ee7b3 100644 --- a/backend/hardhat.config.js +++ b/backend/hardhat.config.js @@ -1,12 +1,12 @@ -require('dotenv').config() -require('@nomicfoundation/hardhat-ethers') -require('@nomicfoundation/hardhat-chai-matchers') +require("dotenv").config(); +require("@nomicfoundation/hardhat-ethers"); +require("@nomicfoundation/hardhat-chai-matchers"); module.exports = { - solidity: "0.8.0", + solidity: "0.8.19", networks: { sepolia: { - url: process.env.SEPOLIA_URL, + url: process.env.ETHEREUM_NETWORK_URL, accounts: [process.env.PRIVATE_KEY] } } diff --git a/backend/package.json b/backend/package.json index 1a66517..fdf0365 100644 --- a/backend/package.json +++ b/backend/package.json @@ -2,7 +2,6 @@ "name": "backend", "version": "1.0.0", "license": "MIT", - "type": "module", "scripts": { "compile": "hardhat compile", "deploy": "hardhat run scripts/deploy.js --network sepolia", @@ -14,16 +13,16 @@ "cors": "^2.8.5", "express": "^4.18.3", "express-session": "^1.18.0", - "hardhat": "^2.20.1", - "siwe": "^3.0.0", - "nodemon": "^3.1.0" + "nodemon": "^3.1.0", + "siwe": "^3.0.0" }, "devDependencies": { + "hardhat": "^2.21.0", "@nomicfoundation/hardhat-ethers": "^3.0.5", "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", - "@types/sinon-chai": "^3.2.3", + "@openzeppelin/contracts": "4.9.3", "chai": "4.3.7", "dotenv": "^16.4.7", "ethers": "^6.11.1" } -} \ No newline at end of file +} diff --git a/backend/scripts/deploy.js b/backend/scripts/deploy.js index 559bc6b..7224e86 100644 --- a/backend/scripts/deploy.js +++ b/backend/scripts/deploy.js @@ -1,12 +1,17 @@ +const hre = require("hardhat"); + async function main() { - const [deployer] = await ethers.getSigners(); + console.log("Начинаем деплой контракта..."); - console.log('Deploying contracts with the account:', deployer.address); + // Получаем контракт + const MyContract = await hre.ethers.getContractFactory("MyContract"); + + // Деплоим контракт + const myContract = await MyContract.deploy(); + await myContract.waitForDeployment(); - const MyContract = await ethers.getContractFactory('MyContract'); - const contract = await MyContract.deploy(); - - console.log('Contract deployed to:', contract.address); + const address = await myContract.getAddress(); + console.log("Контракт развернут по адресу:", address); } main() diff --git a/backend/server.js b/backend/server.js index b2c74ab..c0a9c82 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,12 +1,8 @@ -import express from 'express'; -import cors from 'cors'; -import session from 'express-session'; -import { SiweMessage, generateNonce } from 'siwe'; -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const express = require('express'); +const cors = require('cors'); +const session = require('express-session'); +const { SiweMessage, generateNonce } = require('siwe'); +const path = require('path'); const app = express(); @@ -69,57 +65,37 @@ app.post('/verify', async (req, res) => { throw new Error('Invalid session'); } - if (!signature || !message) { - console.error('Отсутствует подпись или сообщение'); - throw new Error('Invalid signature or message'); - } - - // Создаем и верифицируем SIWE сообщение - console.log('Начинаем парсинг SIWE сообщения...'); - const siweMessage = new SiweMessage(message); - - console.log('Парсинг успешен:', { - domain: siweMessage.domain, - address: siweMessage.address, - nonce: siweMessage.nonce - }); - - const { success, data: fields } = await siweMessage.verify({ - signature, - domain: siweMessage.domain, - nonce: req.session.nonce - }); - - console.log('Результат верификации:', { success, fields }); - - if (!success) { - throw new Error('Signature verification failed'); - } - - // Сохраняем сессию - req.session.authenticated = true; - req.session.siwe = fields; - - console.log('Сессия сохранена:', { - authenticated: true, - address: fields.address - }); - - req.session.save(() => { - console.log('Session saved successfully'); - res.status(200).json({ + let siweMessage; + try { + siweMessage = new SiweMessage(message); + const fields = await siweMessage.validate(signature); + + if (fields.nonce !== req.session.nonce) { + console.error('Nonce не совпадает'); + throw new Error('Invalid nonce'); + } + + console.log('Сообщение успешно верифицировано'); + req.session.siwe = fields; + req.session.authenticated = true; + req.session.nonce = null; + + res.json({ success: true, - address: fields.address + address: fields.address }); - }); + } catch (error) { + console.error('Ошибка валидации сообщения:', error); + req.session.authenticated = false; + req.session.siwe = null; + req.session.nonce = null; + throw error; + } } catch (error) { console.error('Ошибка верификации:', error); - req.session.authenticated = false; - req.session.nonce = null; - req.session.siwe = null; res.status(400).json({ - error: 'Verification failed', - message: error.message + success: false, + error: error.message }); } }); @@ -184,7 +160,7 @@ app.use((err, req, res, next) => { }); }); -const PORT = 3000; +const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`SIWE сервер запущен на порту ${PORT}`); console.log('Доступные эндпоинты:'); diff --git a/backend/yarn.lock b/backend/yarn.lock index 598dd47..e59c129 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -371,6 +371,11 @@ "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.2" "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.2" +"@openzeppelin/contracts@4.9.3": + version "4.9.3" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.9.3.tgz#00d7a8cf35a475b160b3f0293a6403c511099364" + integrity sha512-He3LieZ1pP2TNt5JbkPA4PNT9WC3gOTOlDcFGJW4Le4QKqwmiNJCRt44APfxMxvq7OugU/cqYuPcSBzOw38DAg== + "@scure/base@~1.1.0": version "1.1.9" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" @@ -560,26 +565,6 @@ dependencies: "@types/node" "*" -"@types/sinon-chai@^3.2.3": - version "3.2.12" - resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.12.tgz#c7cb06bee44a534ec84f3a5534c3a3a46fd779b6" - integrity sha512-9y0Gflk3b0+NhQZ/oxGtaAJDvRywCa5sIyaVnounqLvmf93yBF4EgIRspePtkMs3Tr844nCclYMlcCNmLCvjuQ== - dependencies: - "@types/chai" "*" - "@types/sinon" "*" - -"@types/sinon@*": - version "17.0.3" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.3.tgz#9aa7e62f0a323b9ead177ed23a36ea757141a5fa" - integrity sha512-j3uovdn8ewky9kRBG19bOwaZbexJu/XjtkHyjvUgt4xfPFz18dcORIMqnYh66Fx3Powhcr85NT5+er3+oViapw== - dependencies: - "@types/sinonjs__fake-timers" "*" - -"@types/sinonjs__fake-timers@*": - version "8.1.5" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2" - integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== - accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -1448,7 +1433,7 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -hardhat@^2.20.1: +hardhat@^2.21.0: version "2.22.18" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.18.tgz#e299a26a67b521bbb225370eb47a032d4e097e3a" integrity sha512-2+kUz39gvMo56s75cfLBhiFedkQf+gXdrwCcz4R/5wW0oBdwiyfj2q9BIkMoaA0WIGYYMU2I1Cc4ucTunhfjzw== diff --git a/frontend/src/components/ContractInteraction.vue b/frontend/src/components/ContractInteraction.vue index 8d6b703..00206bf 100644 --- a/frontend/src/components/ContractInteraction.vue +++ b/frontend/src/components/ContractInteraction.vue @@ -105,7 +105,7 @@ const isAuthenticated = ref(false) // Константы const SEPOLIA_CHAIN_ID = 11155111 const provider = new JsonRpcProvider(import.meta.env.VITE_APP_ETHEREUM_NETWORK_URL) -const contractAddress = '0xD1789d2E00e4af3157330ADFbb813427696c8A01' +const contractAddress = '0xFF7602583E82C097Ae548Fc8B894F0a73089985E' const contractABI = [ 'function purchase(uint256 amount) payable', 'function price() view returns (uint256)',