From 90da3a0d12948bc571bacb196ac8b3a415f5308d Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 1 Dec 2025 22:05:31 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B2=D0=B0=D1=88=D0=B5=20=D1=81=D0=BE=D0=BE?= =?UTF-8?q?=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 26 +- backend/contracts/DLE_flattened.sol | 503 +++++++++++------- backend/hardhat.config.js | 66 +-- .../modules-deploy-summary.json | 64 +++ backend/scripts/deploy/deploy-modules.js | 111 ++-- backend/scripts/deploy/deploy-multichain.js | 44 +- .../smartcontracts/AddModuleFormView.vue | 2 +- .../views/smartcontracts/AnalyticsView.vue | 2 +- .../smartcontracts/CreateProposalView.vue | 2 +- .../src/views/smartcontracts/HistoryView.vue | 2 +- 10 files changed, 475 insertions(+), 347 deletions(-) create mode 100644 backend/scripts/contracts-data/modules-deploy-summary.json diff --git a/.gitignore b/.gitignore index b3a3ab8..1783d91 100644 --- a/.gitignore +++ b/.gitignore @@ -98,9 +98,6 @@ typings/ *.swp *.swo -# Backup files -*.bak - # OS .DS_Store Thumbs.db @@ -125,12 +122,6 @@ frontend/uploads/ # Database migrations (may contain sensitive data) backend/db/migrations/ -# Keys and certificates -*.key -*.pem -*.crt -*.p12 - # SSH Keys - КРИТИЧЕСКИ ВАЖНО! id_rsa id_ed25519 @@ -138,14 +129,16 @@ id_ed25519 ssh_host_* ssh_config -# SSL Keys and certificates - КРИТИЧЕСКИ ВАЖНО! -ssl/keys/ -ssl/certs/ +# Keys and certificates - КРИТИЧЕСКИ ВАЖНО! *.key *.pem *.crt *.p12 +# SSL Keys and certificates - КРИТИЧЕСКИ ВАЖНО! +ssl/keys/ +ssl/certs/ + # Database encryption keys - КРИТИЧЕСКИ ВАЖНО! **/full_db_encryption.key **/ssl/full_db_encryption.key @@ -207,17 +200,10 @@ backend/.env.production # Server management scripts - may contain sensitive information scripts/clean-server.sh scripts/export-template-for-release.sh +scripts/replace-release-assets.sh # Docker data and release archives - не коммитить в Git! docker-data/ dle-template.tar.gz dle-template.tar.gz.part-* dle-template.tar.gz.join.sh - -# Guide files -pages-guide.md - -# Problem documentation (internal) -SIWE_PROBLEM.md -REBUILD_COMMANDS.md -SIWE_ISSUE_DESCRIPTION.md \ No newline at end of file diff --git a/backend/contracts/DLE_flattened.sol b/backend/contracts/DLE_flattened.sol index 5c480e1..ab0f71c 100644 --- a/backend/contracts/DLE_flattened.sol +++ b/backend/contracts/DLE_flattened.sol @@ -1,12 +1,12 @@ -// Sources flattened with hardhat v2.26.1 https://hardhat.org +// Sources flattened with hardhat v2.26.3 https://hardhat.org // SPDX-License-Identifier: MIT AND PROPRIETARY -// File @openzeppelin/contracts/governance/utils/IVotes.sol@v5.2.0 +// File @openzeppelin/contracts/governance/utils/IVotes.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.0) (governance/utils/IVotes.sol) -pragma solidity ^0.8.20; +// OpenZeppelin Contracts (last updated v5.4.0) (governance/utils/IVotes.sol) +pragma solidity >=0.8.4; /** * @dev Common interface for {ERC20Votes}, {ERC721Votes}, and other {Votes}-enabled contracts. @@ -65,12 +65,12 @@ interface IVotes { } -// File @openzeppelin/contracts/interfaces/IERC6372.sol@v5.2.0 +// File @openzeppelin/contracts/interfaces/IERC6372.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC6372.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC6372.sol) -pragma solidity ^0.8.20; +pragma solidity >=0.4.16; interface IERC6372 { /** @@ -86,18 +86,18 @@ interface IERC6372 { } -// File @openzeppelin/contracts/interfaces/IERC5805.sol@v5.2.0 +// File @openzeppelin/contracts/interfaces/IERC5805.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5805.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC5805.sol) -pragma solidity ^0.8.20; +pragma solidity >=0.8.4; interface IERC5805 is IERC6372, IVotes {} -// File @openzeppelin/contracts/utils/Context.sol@v5.2.0 +// File @openzeppelin/contracts/utils/Context.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) @@ -129,7 +129,7 @@ abstract contract Context { } -// File @openzeppelin/contracts/utils/cryptography/ECDSA.sol@v5.2.0 +// File @openzeppelin/contracts/utils/cryptography/ECDSA.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/ECDSA.sol) @@ -313,12 +313,12 @@ library ECDSA { } -// File @openzeppelin/contracts/interfaces/IERC5267.sol@v5.2.0 +// File @openzeppelin/contracts/interfaces/IERC5267.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC5267.sol) -pragma solidity ^0.8.20; +pragma solidity >=0.4.16; interface IERC5267 { /** @@ -345,7 +345,7 @@ interface IERC5267 { } -// File @openzeppelin/contracts/utils/math/SafeCast.sol@v5.2.0 +// File @openzeppelin/contracts/utils/math/SafeCast.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol) @@ -1511,7 +1511,7 @@ library SafeCast { } -// File @openzeppelin/contracts/utils/Panic.sol@v5.2.0 +// File @openzeppelin/contracts/utils/Panic.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol) @@ -1572,10 +1572,10 @@ library Panic { } -// File @openzeppelin/contracts/utils/math/Math.sol@v5.2.0 +// File @openzeppelin/contracts/utils/math/Math.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol) +// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol) pragma solidity ^0.8.20; @@ -1592,38 +1592,68 @@ library Math { } /** - * @dev Returns the addition of two unsigned integers, with an success flag (no overflow). + * @dev Return the 512-bit addition of two uint256. + * + * The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low. + */ + function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) { + assembly ("memory-safe") { + low := add(a, b) + high := lt(low, a) + } + } + + /** + * @dev Return the 512-bit multiplication of two uint256. + * + * The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low. + */ + function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) { + // 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use + // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 + // variables such that product = high * 2²⁵⁶ + low. + assembly ("memory-safe") { + let mm := mulmod(a, b, not(0)) + low := mul(a, b) + high := sub(sub(mm, low), lt(mm, low)) + } + } + + /** + * @dev Returns the addition of two unsigned integers, with a success flag (no overflow). */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { uint256 c = a + b; - if (c < a) return (false, 0); - return (true, c); + success = c >= a; + result = c * SafeCast.toUint(success); } } /** - * @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow). + * @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow). */ function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { - if (b > a) return (false, 0); - return (true, a - b); + uint256 c = a - b; + success = c <= a; + result = c * SafeCast.toUint(success); } } /** - * @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow). + * @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow). */ function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { - // Gas optimization: this is cheaper than requiring 'a' not being zero, but the - // benefit is lost if 'b' is also tested. - // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 - if (a == 0) return (true, 0); uint256 c = a * b; - if (c / a != b) return (false, 0); - return (true, c); + assembly ("memory-safe") { + // Only true when the multiplication doesn't overflow + // (c / a == b) || (a == 0) + success := or(eq(div(c, a), b), iszero(a)) + } + // equivalent to: success ? c : 0 + result = c * SafeCast.toUint(success); } } @@ -1632,8 +1662,11 @@ library Math { */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { - if (b == 0) return (false, 0); - return (true, a / b); + success = b > 0; + assembly ("memory-safe") { + // The `DIV` opcode returns zero when the denominator is 0. + result := div(a, b) + } } } @@ -1642,11 +1675,38 @@ library Math { */ function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { unchecked { - if (b == 0) return (false, 0); - return (true, a % b); + success = b > 0; + assembly ("memory-safe") { + // The `MOD` opcode returns zero when the denominator is 0. + result := mod(a, b) + } } } + /** + * @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing. + */ + function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) { + (bool success, uint256 result) = tryAdd(a, b); + return ternary(success, result, type(uint256).max); + } + + /** + * @dev Unsigned saturating subtraction, bounds to zero instead of overflowing. + */ + function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) { + (, uint256 result) = trySub(a, b); + return result; + } + + /** + * @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing. + */ + function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) { + (bool success, uint256 result) = tryMul(a, b); + return ternary(success, result, type(uint256).max); + } + /** * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant. * @@ -1717,26 +1777,18 @@ library Math { */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { - // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use - // the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 - // variables such that product = prod1 * 2²⁵⁶ + prod0. - uint256 prod0 = x * y; // Least significant 256 bits of the product - uint256 prod1; // Most significant 256 bits of the product - assembly { - let mm := mulmod(x, y, not(0)) - prod1 := sub(sub(mm, prod0), lt(mm, prod0)) - } + (uint256 high, uint256 low) = mul512(x, y); // Handle non-overflow cases, 256 by 256 division. - if (prod1 == 0) { + if (high == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. - return prod0 / denominator; + return low / denominator; } // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0. - if (denominator <= prod1) { + if (denominator <= high) { Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW)); } @@ -1744,34 +1796,34 @@ library Math { // 512 by 256 division. /////////////////////////////////////////////// - // Make division exact by subtracting the remainder from [prod1 prod0]. + // Make division exact by subtracting the remainder from [high low]. uint256 remainder; - assembly { + assembly ("memory-safe") { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. - prod1 := sub(prod1, gt(remainder, prod0)) - prod0 := sub(prod0, remainder) + high := sub(high, gt(remainder, low)) + low := sub(low, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); - assembly { + assembly ("memory-safe") { // Divide denominator by twos. denominator := div(denominator, twos) - // Divide [prod1 prod0] by twos. - prod0 := div(prod0, twos) + // Divide [high low] by twos. + low := div(low, twos) // Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } - // Shift in bits from prod1 into prod0. - prod0 |= prod1 * twos; + // Shift in bits from high into low. + low |= high * twos; // Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such // that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for @@ -1789,9 +1841,9 @@ library Math { // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is - // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1 + // less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high // is no longer required. - result = prod0 * inverse; + result = low * inverse; return result; } } @@ -1803,6 +1855,26 @@ library Math { return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0); } + /** + * @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256. + */ + function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) { + unchecked { + (uint256 high, uint256 low) = mul512(x, y); + if (high >= 1 << n) { + Panic.panic(Panic.UNDER_OVERFLOW); + } + return (high << (256 - n)) | (low >> n); + } + } + + /** + * @dev Calculates x * y >> n with full precision, following the selected rounding direction. + */ + function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) { + return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0); + } + /** * @dev Calculate the modular multiplicative inverse of a number in Z/nZ. * @@ -2111,41 +2183,45 @@ library Math { * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ - function log2(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - uint256 exp; - unchecked { - exp = 128 * SafeCast.toUint(value > (1 << 128) - 1); - value >>= exp; - result += exp; + function log2(uint256 x) internal pure returns (uint256 r) { + // If value has upper 128 bits set, log2 result is at least 128 + r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7; + // If upper 64 bits of 128-bit half set, add 64 to result + r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6; + // If upper 32 bits of 64-bit half set, add 32 to result + r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5; + // If upper 16 bits of 32-bit half set, add 16 to result + r |= SafeCast.toUint((x >> r) > 0xffff) << 4; + // If upper 8 bits of 16-bit half set, add 8 to result + r |= SafeCast.toUint((x >> r) > 0xff) << 3; + // If upper 4 bits of 8-bit half set, add 4 to result + r |= SafeCast.toUint((x >> r) > 0xf) << 2; - exp = 64 * SafeCast.toUint(value > (1 << 64) - 1); - value >>= exp; - result += exp; - - exp = 32 * SafeCast.toUint(value > (1 << 32) - 1); - value >>= exp; - result += exp; - - exp = 16 * SafeCast.toUint(value > (1 << 16) - 1); - value >>= exp; - result += exp; - - exp = 8 * SafeCast.toUint(value > (1 << 8) - 1); - value >>= exp; - result += exp; - - exp = 4 * SafeCast.toUint(value > (1 << 4) - 1); - value >>= exp; - result += exp; - - exp = 2 * SafeCast.toUint(value > (1 << 2) - 1); - value >>= exp; - result += exp; - - result += SafeCast.toUint(value > 1); + // Shifts value right by the current result and use it as an index into this lookup table: + // + // | x (4 bits) | index | table[index] = MSB position | + // |------------|---------|-----------------------------| + // | 0000 | 0 | table[0] = 0 | + // | 0001 | 1 | table[1] = 0 | + // | 0010 | 2 | table[2] = 1 | + // | 0011 | 3 | table[3] = 1 | + // | 0100 | 4 | table[4] = 2 | + // | 0101 | 5 | table[5] = 2 | + // | 0110 | 6 | table[6] = 2 | + // | 0111 | 7 | table[7] = 2 | + // | 1000 | 8 | table[8] = 3 | + // | 1001 | 9 | table[9] = 3 | + // | 1010 | 10 | table[10] = 3 | + // | 1011 | 11 | table[11] = 3 | + // | 1100 | 12 | table[12] = 3 | + // | 1101 | 13 | table[13] = 3 | + // | 1110 | 14 | table[14] = 3 | + // | 1111 | 15 | table[15] = 3 | + // + // The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes. + assembly ("memory-safe") { + r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000)) } - return result; } /** @@ -2214,29 +2290,17 @@ library Math { * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ - function log256(uint256 value) internal pure returns (uint256) { - uint256 result = 0; - uint256 isGt; - unchecked { - isGt = SafeCast.toUint(value > (1 << 128) - 1); - value >>= isGt * 128; - result += isGt * 16; - - isGt = SafeCast.toUint(value > (1 << 64) - 1); - value >>= isGt * 64; - result += isGt * 8; - - isGt = SafeCast.toUint(value > (1 << 32) - 1); - value >>= isGt * 32; - result += isGt * 4; - - isGt = SafeCast.toUint(value > (1 << 16) - 1); - value >>= isGt * 16; - result += isGt * 2; - - result += SafeCast.toUint(value > (1 << 8) - 1); - } - return result; + function log256(uint256 x) internal pure returns (uint256 r) { + // If value has upper 128 bits set, log2 result is at least 128 + r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7; + // If upper 64 bits of 128-bit half set, add 64 to result + r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6; + // If upper 32 bits of 64-bit half set, add 32 to result + r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5; + // If upper 16 bits of 32-bit half set, add 16 to result + r |= SafeCast.toUint((x >> r) > 0xffff) << 4; + // Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8 + return (r >> 3) | SafeCast.toUint((x >> r) > 0xff); } /** @@ -2259,7 +2323,7 @@ library Math { } -// File @openzeppelin/contracts/utils/math/SignedMath.sol@v5.2.0 +// File @openzeppelin/contracts/utils/math/SignedMath.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol) @@ -2329,10 +2393,10 @@ library SignedMath { } -// File @openzeppelin/contracts/utils/Strings.sol@v5.2.0 +// File @openzeppelin/contracts/utils/Strings.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.2.0) (utils/Strings.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (utils/Strings.sol) pragma solidity ^0.8.20; @@ -2346,6 +2410,14 @@ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; + uint256 private constant SPECIAL_CHARS_LOOKUP = + (1 << 0x08) | // backspace + (1 << 0x09) | // tab + (1 << 0x0a) | // newline + (1 << 0x0c) | // form feed + (1 << 0x0d) | // carriage return + (1 << 0x22) | // double quote + (1 << 0x5c); // backslash /** * @dev The `value` string doesn't fit in the specified `length`. @@ -2371,7 +2443,7 @@ library Strings { string memory buffer = new string(length); uint256 ptr; assembly ("memory-safe") { - ptr := add(buffer, add(32, length)) + ptr := add(add(buffer, 0x20), length) } while (true) { ptr--; @@ -2470,7 +2542,7 @@ library Strings { } /** - * @dev Variant of {parseUint} that parses a substring of `input` located between position `begin` (included) and + * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and * `end` (excluded). * * Requirements: @@ -2508,7 +2580,7 @@ library Strings { } /** - * @dev Implementation of {tryParseUint} that does not check bounds. Caller should make sure that + * @dev Implementation of {tryParseUint-string-uint256-uint256} that does not check bounds. Caller should make sure that * `begin <= end <= input.length`. Other inputs would result in undefined behavior. */ function _tryParseUintUncheckedBounds( @@ -2581,7 +2653,7 @@ library Strings { } /** - * @dev Implementation of {tryParseInt} that does not check bounds. Caller should make sure that + * @dev Implementation of {tryParseInt-string-uint256-uint256} that does not check bounds. Caller should make sure that * `begin <= end <= input.length`. Other inputs would result in undefined behavior. */ function _tryParseIntUncheckedBounds( @@ -2618,7 +2690,7 @@ library Strings { } /** - * @dev Variant of {parseHexUint} that parses a substring of `input` located between position `begin` (included) and + * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and * `end` (excluded). * * Requirements: @@ -2656,7 +2728,7 @@ library Strings { } /** - * @dev Implementation of {tryParseHexUint} that does not check bounds. Caller should make sure that + * @dev Implementation of {tryParseHexUint-string-uint256-uint256} that does not check bounds. Caller should make sure that * `begin <= end <= input.length`. Other inputs would result in undefined behavior. */ function _tryParseHexUintUncheckedBounds( @@ -2677,7 +2749,7 @@ library Strings { result *= 16; unchecked { // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check). - // This guaratees that adding a value < 16 will not cause an overflow, hence the unchecked. + // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked. result += chr; } } @@ -2695,7 +2767,7 @@ library Strings { } /** - * @dev Variant of {parseAddress} that parses a substring of `input` located between position `begin` (included) and + * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and * `end` (excluded). * * Requirements: @@ -2709,7 +2781,7 @@ library Strings { /** * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly - * formatted address. See {parseAddress} requirements. + * formatted address. See {parseAddress-string} requirements. */ function tryParseAddress(string memory input) internal pure returns (bool success, address value) { return tryParseAddress(input, 0, bytes(input).length); @@ -2717,7 +2789,7 @@ library Strings { /** * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly - * formatted address. See {parseAddress} requirements. + * formatted address. See {parseAddress-string-uint256-uint256} requirements. */ function tryParseAddress( string memory input, @@ -2757,6 +2829,47 @@ library Strings { return value; } + /** + * @dev Escape special characters in JSON strings. This can be useful to prevent JSON injection in NFT metadata. + * + * WARNING: This function should only be used in double quoted JSON strings. Single quotes are not escaped. + * + * NOTE: This function escapes all unicode characters, and not just the ones in ranges defined in section 2.5 of + * RFC-4627 (U+0000 to U+001F, U+0022 and U+005C). ECMAScript's `JSON.parse` does recover escaped unicode + * characters that are not in this range, but other tooling may provide different results. + */ + function escapeJSON(string memory input) internal pure returns (string memory) { + bytes memory buffer = bytes(input); + bytes memory output = new bytes(2 * buffer.length); // worst case scenario + uint256 outputLength = 0; + + for (uint256 i; i < buffer.length; ++i) { + bytes1 char = bytes1(_unsafeReadBytesOffset(buffer, i)); + if (((SPECIAL_CHARS_LOOKUP & (1 << uint8(char))) != 0)) { + output[outputLength++] = "\\"; + if (char == 0x08) output[outputLength++] = "b"; + else if (char == 0x09) output[outputLength++] = "t"; + else if (char == 0x0a) output[outputLength++] = "n"; + else if (char == 0x0c) output[outputLength++] = "f"; + else if (char == 0x0d) output[outputLength++] = "r"; + else if (char == 0x5c) output[outputLength++] = "\\"; + else if (char == 0x22) { + // solhint-disable-next-line quotes + output[outputLength++] = '"'; + } + } else { + output[outputLength++] = char; + } + } + // write the actual length and deallocate unused memory + assembly ("memory-safe") { + mstore(output, outputLength) + mstore(0x40, add(output, shl(5, shr(5, add(outputLength, 63))))) + } + + return string(output); + } + /** * @dev Reads a bytes32 from a bytes array without bounds checking. * @@ -2766,16 +2879,16 @@ library Strings { function _unsafeReadBytesOffset(bytes memory buffer, uint256 offset) private pure returns (bytes32 value) { // This is not memory safe in the general case, but all calls to this private function are within bounds. assembly ("memory-safe") { - value := mload(add(buffer, add(0x20, offset))) + value := mload(add(add(buffer, 0x20), offset)) } } } -// File @openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol@v5.2.0 +// File @openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/MessageHashUtils.sol) +// OpenZeppelin Contracts (last updated v5.3.0) (utils/cryptography/MessageHashUtils.sol) pragma solidity ^0.8.20; @@ -2793,7 +2906,7 @@ library MessageHashUtils { * * The digest is calculated by prefixing a bytes32 `messageHash` with * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the - * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. + * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method. * * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * keccak256, although any bytes32 value can be safely used because the final digest will @@ -2815,7 +2928,7 @@ library MessageHashUtils { * * The digest is calculated by prefixing an arbitrary `message` with * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the - * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. + * hash signed when using the https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign[`eth_sign`] JSON-RPC method. * * See {ECDSA-recover}. */ @@ -2837,6 +2950,21 @@ library MessageHashUtils { return keccak256(abi.encodePacked(hex"19_00", validator, data)); } + /** + * @dev Variant of {toDataWithIntendedValidatorHash-address-bytes} optimized for cases where `data` is a bytes32. + */ + function toDataWithIntendedValidatorHash( + address validator, + bytes32 messageHash + ) internal pure returns (bytes32 digest) { + assembly ("memory-safe") { + mstore(0x00, hex"19_00") + mstore(0x02, shl(96, validator)) + mstore(0x16, messageHash) + digest := keccak256(0x00, 0x36) + } + } + /** * @dev Returns the keccak256 digest of an EIP-712 typed data (ERC-191 version `0x01`). * @@ -2858,7 +2986,7 @@ library MessageHashUtils { } -// File @openzeppelin/contracts/utils/StorageSlot.sol@v5.2.0 +// File @openzeppelin/contracts/utils/StorageSlot.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol) @@ -3005,10 +3133,10 @@ library StorageSlot { } -// File @openzeppelin/contracts/utils/ShortStrings.sol@v5.2.0 +// File @openzeppelin/contracts/utils/ShortStrings.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (utils/ShortStrings.sol) +// OpenZeppelin Contracts (last updated v5.3.0) (utils/ShortStrings.sol) pragma solidity ^0.8.20; @@ -3102,7 +3230,7 @@ library ShortStrings { } /** - * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}. + * @dev Decode a string that was encoded to `ShortString` or written to storage using {toShortStringWithFallback}. */ function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) { if (ShortString.unwrap(value) != FALLBACK_SENTINEL) { @@ -3114,7 +3242,7 @@ library ShortStrings { /** * @dev Return the length of a string that was encoded to `ShortString` or written to storage using - * {setWithFallback}. + * {toShortStringWithFallback}. * * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of * actual characters as the UTF-8 encoding of a single character can span over multiple bytes. @@ -3129,10 +3257,10 @@ library ShortStrings { } -// File @openzeppelin/contracts/utils/cryptography/EIP712.sol@v5.2.0 +// File @openzeppelin/contracts/utils/cryptography/EIP712.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (utils/cryptography/EIP712.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.20; @@ -3179,7 +3307,9 @@ abstract contract EIP712 is IERC5267 { ShortString private immutable _name; ShortString private immutable _version; + // slither-disable-next-line constable-states string private _nameFallback; + // slither-disable-next-line constable-states string private _versionFallback; /** @@ -3239,9 +3369,7 @@ abstract contract EIP712 is IERC5267 { return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash); } - /** - * @dev See {IERC-5267}. - */ + /// @inheritdoc IERC5267 function eip712Domain() public view @@ -3291,7 +3419,7 @@ abstract contract EIP712 is IERC5267 { } -// File @openzeppelin/contracts/utils/Nonces.sol@v5.2.0 +// File @openzeppelin/contracts/utils/Nonces.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol) @@ -3341,10 +3469,10 @@ abstract contract Nonces { } -// File @openzeppelin/contracts/utils/structs/Checkpoints.sol@v5.2.0 +// File @openzeppelin/contracts/utils/structs/Checkpoints.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/Checkpoints.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/Checkpoints.sol) // This file was procedurally generated from scripts/generate/templates/Checkpoints.js. pragma solidity ^0.8.20; @@ -3411,7 +3539,7 @@ library Checkpoints { * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero * if there is none. * - * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high + * NOTE: This is a variant of {upperLookup} that is optimized to find "recent" checkpoint (checkpoints with high * keys). */ function upperLookupRecent(Trace224 storage self, uint32 key) internal view returns (uint224) { @@ -3457,7 +3585,7 @@ library Checkpoints { } /** - * @dev Returns the number of checkpoint. + * @dev Returns the number of checkpoints. */ function length(Trace224 storage self) internal view returns (uint256) { return self._checkpoints.length; @@ -3614,7 +3742,7 @@ library Checkpoints { * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero * if there is none. * - * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high + * NOTE: This is a variant of {upperLookup} that is optimized to find "recent" checkpoint (checkpoints with high * keys). */ function upperLookupRecent(Trace208 storage self, uint48 key) internal view returns (uint208) { @@ -3660,7 +3788,7 @@ library Checkpoints { } /** - * @dev Returns the number of checkpoint. + * @dev Returns the number of checkpoints. */ function length(Trace208 storage self) internal view returns (uint256) { return self._checkpoints.length; @@ -3817,7 +3945,7 @@ library Checkpoints { * @dev Returns the value in the last (most recent) checkpoint with key lower or equal than the search key, or zero * if there is none. * - * NOTE: This is a variant of {upperLookup} that is optimised to find "recent" checkpoint (checkpoints with high + * NOTE: This is a variant of {upperLookup} that is optimized to find "recent" checkpoint (checkpoints with high * keys). */ function upperLookupRecent(Trace160 storage self, uint96 key) internal view returns (uint160) { @@ -3863,7 +3991,7 @@ library Checkpoints { } /** - * @dev Returns the number of checkpoint. + * @dev Returns the number of checkpoints. */ function length(Trace160 storage self) internal view returns (uint256) { return self._checkpoints.length; @@ -3973,7 +4101,7 @@ library Checkpoints { } -// File @openzeppelin/contracts/utils/types/Time.sol@v5.2.0 +// File @openzeppelin/contracts/utils/types/Time.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/types/Time.sol) @@ -4108,7 +4236,7 @@ library Time { } -// File @openzeppelin/contracts/governance/utils/Votes.sol@v5.2.0 +// File @openzeppelin/contracts/governance/utils/Votes.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.2.0) (governance/utils/Votes.sol) @@ -4362,11 +4490,11 @@ abstract contract Votes is Context, EIP712, Nonces, IERC5805 { } -// File @openzeppelin/contracts/interfaces/draft-IERC6093.sol@v5.2.0 +// File @openzeppelin/contracts/interfaces/draft-IERC6093.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol) -pragma solidity ^0.8.20; +// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol) +pragma solidity >=0.8.4; /** * @dev Standard ERC-20 Errors @@ -4527,12 +4655,12 @@ interface IERC1155Errors { } -// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v5.2.0 +// File @openzeppelin/contracts/token/ERC20/IERC20.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol) -pragma solidity ^0.8.20; +pragma solidity >=0.4.16; /** * @dev Interface of the ERC-20 standard as defined in the ERC. @@ -4610,12 +4738,12 @@ interface IERC20 { } -// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v5.2.0 +// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol) -pragma solidity ^0.8.20; +pragma solidity >=0.6.2; /** * @dev Interface for the optional metadata functions from the ERC-20 standard. @@ -4638,10 +4766,10 @@ interface IERC20Metadata is IERC20 { } -// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v5.2.0 +// File @openzeppelin/contracts/token/ERC20/ERC20.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; @@ -4679,8 +4807,7 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { /** * @dev Sets the values for {name} and {symbol}. * - * All two of these values are immutable: they can only be set once during - * construction. + * Both values are immutable: they can only be set once during construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; @@ -4719,16 +4846,12 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { return 18; } - /** - * @dev See {IERC20-totalSupply}. - */ + /// @inheritdoc IERC20 function totalSupply() public view virtual returns (uint256) { return _totalSupply; } - /** - * @dev See {IERC20-balanceOf}. - */ + /// @inheritdoc IERC20 function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } @@ -4747,9 +4870,7 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { return true; } - /** - * @dev See {IERC20-allowance}. - */ + /// @inheritdoc IERC20 function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } @@ -4881,7 +5002,7 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { } /** - * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. + * @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. @@ -4931,7 +5052,7 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { } /** - * @dev Updates `owner` s allowance for `spender` based on spent `value`. + * @dev Updates `owner`'s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. @@ -4952,12 +5073,12 @@ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { } -// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol@v5.2.0 +// File @openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Permit.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Permit.sol) -pragma solidity ^0.8.20; +pragma solidity >=0.4.16; /** * @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in @@ -5046,10 +5167,10 @@ interface IERC20Permit { } -// File @openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol@v5.2.0 +// File @openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT -// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC20Permit.sol) +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/ERC20Permit.sol) pragma solidity ^0.8.20; @@ -5086,9 +5207,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces { */ constructor(string memory name) EIP712(name, "1") {} - /** - * @inheritdoc IERC20Permit - */ + /// @inheritdoc IERC20Permit function permit( address owner, address spender, @@ -5114,16 +5233,12 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces { _approve(owner, spender, value); } - /** - * @inheritdoc IERC20Permit - */ + /// @inheritdoc IERC20Permit function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) { return super.nonces(owner); } - /** - * @inheritdoc IERC20Permit - */ + /// @inheritdoc IERC20Permit // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view virtual returns (bytes32) { return _domainSeparatorV4(); @@ -5131,7 +5246,7 @@ abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces { } -// File @openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol@v5.2.0 +// File @openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/ERC20Votes.sol) @@ -5216,7 +5331,7 @@ abstract contract ERC20Votes is ERC20, Votes { } -// File @openzeppelin/contracts/utils/ReentrancyGuard.sol@v5.2.0 +// File @openzeppelin/contracts/utils/ReentrancyGuard.sol@v5.4.0 // Original license: SPDX_License_Identifier: MIT // OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol) diff --git a/backend/hardhat.config.js b/backend/hardhat.config.js index 4de03d9..4bb87e0 100644 --- a/backend/hardhat.config.js +++ b/backend/hardhat.config.js @@ -26,41 +26,45 @@ function getNetworks() { // Получаем RPC URLs из переменных окружения const rpcUrlsEnv = process.env.RPC_URLS; const rpcUrls = rpcUrlsEnv ? JSON.parse(rpcUrlsEnv) : {}; - - // console.log удален - может мешать flatten - - // Базовые сети - УБРАНО, используем только базу данных - const baseNetworks = {}; // Пустой объект - никаких хардкод цепочек - - // Если есть supported_chain_ids, фильтруем только нужные сети - if (supportedChainIds.length > 0) { - const networks = {}; - const supportedChainIdsNumbers = supportedChainIds.map(id => Number(id)); - - for (const [networkName, networkConfig] of Object.entries(baseNetworks)) { - if (supportedChainIdsNumbers.includes(networkConfig.chainId)) { - // Используем RPC URL из переменных окружения если есть - const customRpcUrl = rpcUrls[networkConfig.chainId] || rpcUrls[networkConfig.chainId.toString()]; - if (customRpcUrl) { - networkConfig.url = customRpcUrl; - // console.log удален - может мешать flatten - } - networks[networkName] = networkConfig; - } - } - - // console.log удален - может мешать flatten - return networks; - } else { - // Если нет supported_chain_ids, используем все базовые сети - // console.log удален - может мешать flatten - return baseNetworks; + + // Если нет supported_chain_ids, ничего не настраиваем (используются только локальные сети Hardhat) + if (!supportedChainIds || supportedChainIds.length === 0) { + return {}; } + + const networks = {}; + const supportedChainIdsNumbers = supportedChainIds.map((id) => Number(id)); + + // RPC_URLS может быть либо объектом { [chainId]: url }, либо массивом URL-ов. + const isRpcArray = Array.isArray(rpcUrls); + + supportedChainIdsNumbers.forEach((chainId, index) => { + let url; + if (isRpcArray) { + url = rpcUrls[index]; + } else { + url = rpcUrls[chainId] || rpcUrls[chainId.toString()]; + } + + if (!url) { + // Если для chainId нет URL — пропускаем сеть + return; + } + + const networkName = `chain_${chainId}`; + networks[networkName] = { + chainId, + url + // accounts не указываем: для verify достаточно RPC + ETHERSCAN_API_KEY + }; + }); + + return networks; } -// Функция для получения базовых сетей (fallback) - УБРАНО, используем только базу данных +// Функция для получения базовых сетей (fallback) — оставлена для совместимости function getBaseNetworks() { - return {}; // Пустой объект - никаких хардкод цепочек + return {}; } diff --git a/backend/scripts/contracts-data/modules-deploy-summary.json b/backend/scripts/contracts-data/modules-deploy-summary.json new file mode 100644 index 0000000..ca4b45e --- /dev/null +++ b/backend/scripts/contracts-data/modules-deploy-summary.json @@ -0,0 +1,64 @@ +{ + "deploymentId": "modules-deploy-1764614264868", + "dleAddress": "0x692a9B9520e63F0CC3f4d2E0458cd4d82cCFd437", + "dleName": "DLE-TEST", + "dleSymbol": "DLET", + "dleLocation": "101000, Москва, Москва, Тверская, 1, 10", + "dleJurisdiction": 643, + "dleCoordinates": "55.7613657,37.63428", + "dleOktmo": "45000000", + "dleOkvedCodes": [ + "62.01.1" + ], + "dleKpp": "773001001", + "dleLogoURI": "/uploads/logos/default-token.svg", + "dleSupportedChainIds": [ + 11155111, + 421614, + 84532 + ], + "totalNetworks": 3, + "successfulNetworks": 3, + "modulesDeployed": [ + "treasury" + ], + "networks": [ + { + "chainId": 11155111, + "rpcUrl": "https://1rpc.io/sepolia", + "modules": [ + { + "type": "treasury", + "address": "0x9dF6640F52ffCAfD86DE143241565edE069720Bc", + "success": true, + "verification": "verified" + } + ] + }, + { + "chainId": 421614, + "rpcUrl": "https://sepolia-rollup.arbitrum.io/rpc", + "modules": [ + { + "type": "treasury", + "address": "0x9dF6640F52ffCAfD86DE143241565edE069720Bc", + "success": true, + "verification": "verified" + } + ] + }, + { + "chainId": 84532, + "rpcUrl": "https://sepolia.base.org", + "modules": [ + { + "type": "treasury", + "address": "0x9dF6640F52ffCAfD86DE143241565edE069720Bc", + "success": true, + "verification": "verified" + } + ] + } + ], + "timestamp": "2025-12-01T18:37:44.868Z" +} \ No newline at end of file diff --git a/backend/scripts/deploy/deploy-modules.js b/backend/scripts/deploy/deploy-modules.js index c18614d..3fc4ad6 100644 --- a/backend/scripts/deploy/deploy-modules.js +++ b/backend/scripts/deploy/deploy-modules.js @@ -74,32 +74,16 @@ const MODULE_CONFIGS = { // } }; -// Функция для определения имени сети Hardhat по chainId +// Функция для определения имени сети Hardhat по chainId (динамически, без хардкода) +// В hardhat.config.js сети объявляются как chain_ function getNetworkNameForHardhat(chainId) { - const networkMapping = { - 11155111: 'sepolia', - 17000: 'holesky', - 421614: 'arbitrumSepolia', - 84532: 'baseSepolia', - 1: 'mainnet', - 42161: 'arbitrumOne', - 8453: 'base', - 137: 'polygon', - 56: 'bsc' - }; - - const hardhatNetworkName = networkMapping[chainId]; - if (!hardhatNetworkName) { - logger.warn(`⚠️ Сеть ${chainId} не поддерживается в Hardhat`); - return null; - } - - logger.info(`✅ Сеть ${chainId} поддерживается: ${hardhatNetworkName}`); + const hardhatNetworkName = `chain_${Number(chainId)}`; + logger.info(`✅ Сеть ${chainId} будет использовать Hardhat network: ${hardhatNetworkName}`); return hardhatNetworkName; } // Функция для автоматической верификации модуля -async function verifyModuleAfterDeploy(chainId, contractAddress, moduleType, constructorArgs, apiKey) { +async function verifyModuleAfterDeploy(chainId, contractAddress, moduleType, constructorArgs, apiKey, params = {}) { try { if (!apiKey) { logger.warn(`⚠️ API ключ Etherscan не предоставлен, пропускаем верификацию модуля ${moduleType}`); @@ -143,10 +127,17 @@ async function verifyModuleAfterDeploy(chainId, contractAddress, moduleType, con const command = `npx hardhat verify --network ${networkName} --constructor-args ${tempArgsFile} ${contractAddress}`; logger.info(`🔧 Выполняем команду: ${command}`); - // Устанавливаем переменные окружения для Hardhat + // Устанавливаем переменные окружения для Hardhat (в том числе сети и RPC из deploy params) const envVars = { ...process.env, - ETHERSCAN_API_KEY: apiKey + ETHERSCAN_API_KEY: apiKey || params.etherscanApiKey || '', + SUPPORTED_CHAIN_IDS: JSON.stringify( + params.supportedChainIds || params.supported_chain_ids || [chainId] + ), + RPC_URLS: JSON.stringify( + // params.rpcUrls / params.rpc_urls могут быть либо массивом, либо объектом { [chainId]: url } + params.rpcUrls || params.rpc_urls || {} + ) }; const { stdout, stderr } = await execAsync(command, { @@ -428,9 +419,9 @@ async function deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesT }); const net = network; - const chainId = Number(net.chainId); + const numericChainId = Number(net.chainId); - logger.info(`[MODULES_DBG] chainId=${chainId} deploying modules: ${modulesToDeploy.join(', ')}`); + logger.info(`[MODULES_DBG] chainId=${numericChainId} deploying modules: ${modulesToDeploy.join(', ')}`); const results = {}; @@ -443,14 +434,14 @@ async function deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesT logger.info(`[MODULES_DBG] Деплой модуля ${moduleType} в сети ${net.name || net.chainId}`); if (!MODULE_CONFIGS[moduleType]) { - logger.error(`[MODULES_DBG] chainId=${chainId} Unknown module type: ${moduleType}`); + logger.error(`[MODULES_DBG] chainId=${numericChainId} Unknown module type: ${moduleType}`); results[moduleType] = { success: false, error: `Unknown module type: ${moduleType}` }; logger.error(`[MODULES_DBG] Неизвестный тип модуля: ${moduleType}`); continue; } if (!moduleInit) { - logger.error(`[MODULES_DBG] chainId=${chainId} No init code for module: ${moduleType}`); + logger.error(`[MODULES_DBG] chainId=${numericChainId} No init code for module: ${moduleType}`); results[moduleType] = { success: false, error: `No init code for module: ${moduleType}` }; logger.error(`[MODULES_DBG] Отсутствует код инициализации для модуля: ${moduleType}`); continue; @@ -469,7 +460,7 @@ async function deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesT // Получаем аргументы конструктора для модуля const moduleConfig = MODULE_CONFIGS[moduleType]; - const constructorArgs = moduleConfig.constructorArgs(dleAddress, chainId, wallet.address); + const constructorArgs = moduleConfig.constructorArgs(dleAddress, numericChainId, wallet.address); // Ждем 30 секунд перед верификацией, чтобы транзакция получила подтверждения logger.info(`[MODULES_DBG] Ждем 30 секунд перед верификацией модуля ${moduleType}...`); @@ -477,7 +468,7 @@ async function deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesT // Проверяем, что контракт действительно задеплоен try { - const { provider } = await createRPCConnection(rpcUrl, pk, { maxRetries: 3, timeout: 30000 }); + const { provider } = await createRPCConnection(numericChainId, pk, { maxRetries: 3, timeout: 30000 }); const code = await provider.getCode(result.address); if (!code || code === '0x') { logger.warn(`[MODULES_DBG] Контракт ${moduleType} не найден по адресу ${result.address}, пропускаем верификацию`); @@ -489,11 +480,12 @@ async function deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesT } const verificationResult = await verifyModuleAfterDeploy( - chainId, + numericChainId, result.address, moduleType, constructorArgs, - params.etherscanApiKey + params.etherscanApiKey, + params ); if (verificationResult.success) { @@ -542,16 +534,9 @@ async function deployAllModulesInNetwork(chainId, pk, salt, dleAddress, modulesT async function deployAllModulesInAllNetworks(networks, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces) { const results = []; - for (let i = 0; i < connections.length; i++) { - const connection = connections[i]; - const rpcUrl = connection.rpcUrl; - logger.info(`[MODULES_DBG] deploying modules to network ${i + 1}/${connections.length}: ${rpcUrl}`); - - const result = await deployAllModulesInNetwork(rpcUrl, pk, salt, dleAddress, modulesToDeploy, moduleInits, targetNonces); - results.push(result); - } - - return results; + // Функция больше не используется (логика деплоя реализована через connections в main) + // Оставлена для совместимости. + return []; } async function main() { @@ -849,38 +834,22 @@ async function main() { }; // Собираем информацию о всех сетях для этого модуля - for (let i = 0; i < networks.length; i++) { - const rpcUrl = networks[i]; + for (let i = 0; i < deployResults.length; i++) { const deployResult = deployResults[i]; - const verificationResult = deployResult.verification || 'unknown'; + const rpcUrl = deployResult.rpcUrl; const moduleResult = deployResult.modules?.[moduleType]; - const verification = verificationResult?.modules?.[moduleType] || 'unknown'; - - try { - const { provider, network } = await createRPCConnection(rpcUrl, pk, { - maxRetries: 3, - timeout: 30000 - }); - - moduleInfo.networks.push({ - chainId: Number(network.chainId), - rpcUrl: rpcUrl, - address: moduleResult?.success ? moduleResult.address : null, - verification: verification, - success: moduleResult?.success || false, - error: moduleResult?.error || null - }); - } catch (error) { - logger.error(`[MODULES_DBG] Ошибка получения chainId для модуля ${moduleType} в сети ${i + 1}:`, error.message); - moduleInfo.networks.push({ - chainId: null, - rpcUrl: rpcUrl, - address: null, - verification: 'error', - success: false, - error: error.message - }); - } + + // Верификационный статус уже хранится в deployResult.modules[moduleType].verification + const verification = moduleResult?.verification || 'unknown'; + + moduleInfo.networks.push({ + chainId: deployResult.chainId ?? null, + rpcUrl: rpcUrl, + address: moduleResult?.success ? moduleResult.address : null, + verification: verification, + success: moduleResult?.success || false, + error: moduleResult?.error || null + }); } // Сохраняем файл модуля diff --git a/backend/scripts/deploy/deploy-multichain.js b/backend/scripts/deploy/deploy-multichain.js index 35aa463..9d1dfc3 100755 --- a/backend/scripts/deploy/deploy-multichain.js +++ b/backend/scripts/deploy/deploy-multichain.js @@ -54,26 +54,6 @@ console.log('[MULTI_DBG] main:', typeof main); // Функция для получения имени сети для Hardhat из deploy_params function getNetworkNameForHardhat(chainId, params) { - // Создаем маппинг chainId -> Hardhat network name - const networkMapping = { - 11155111: 'sepolia', - 17000: 'holesky', - 421614: 'arbitrumSepolia', - 84532: 'baseSepolia', - 1: 'mainnet', - 42161: 'arbitrumOne', - 8453: 'base', - 137: 'polygon', - 56: 'bsc' - }; - - // Проверяем, поддерживается ли сеть в Hardhat - const hardhatNetworkName = networkMapping[chainId]; - if (!hardhatNetworkName) { - logger.warn(`⚠️ Сеть ${chainId} не поддерживается в Hardhat`); - return null; - } - // Проверяем, есть ли эта сеть в supported_chain_ids из deploy_params const supportedChainIds = params.supported_chain_ids || params.supportedChainIds || []; if (supportedChainIds.length > 0) { @@ -88,7 +68,10 @@ function getNetworkNameForHardhat(chainId, params) { logger.info(`ℹ️ Список поддерживаемых сетей пуст, разрешаем верификацию для ${chainId}`); } - logger.info(`✅ Сеть ${chainId} поддерживается: ${hardhatNetworkName}`); + // Динамически формируем имя сети для Hardhat без хардкода: + // в конфиге Hardhat сеть будет объявлена как chain_ + const hardhatNetworkName = `chain_${chainId}`; + logger.info(`✅ Сеть ${chainId} будет использовать Hardhat network: ${hardhatNetworkName}`); logger.info(`🔍 Детали сети: chainId=${chainId}, hardhatName=${hardhatNetworkName}, supportedChains=[${supportedChainIds.join(', ')}]`); return hardhatNetworkName; } @@ -824,11 +807,16 @@ async function main() { })); // ВЫВОДИМ РЕЗУЛЬТАТ С ИНТЕГРИРОВАННОЙ ВЕРИФИКАЦИЕЙ! - console.log('[MULTI_DBG] 🎯 ДОШЛИ ДО ВЫВОДА РЕЗУЛЬТАТА!'); - console.log('[MULTI_DBG] 📊 finalResults:', JSON.stringify(finalResults, null, 2)); - console.log('[MULTI_DBG] 🎯 ВЫВОДИМ MULTICHAIN_DEPLOY_RESULT!'); - console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify(finalResults)); - console.log('[MULTI_DBG] ✅ MULTICHAIN_DEPLOY_RESULT ВЫВЕДЕН!'); + // Важно: используем process.stdout.write, чтобы обойти маскирование адресов (logger/console) + logger.info('[MULTI_DBG] 🎯 ДОШЛИ ДО ВЫВОДА РЕЗУЛЬТАТА!'); + logger.info('[MULTI_DBG] 📊 finalResults:', finalResults); + logger.info('[MULTI_DBG] 🎯 ВЫВОДИМ MULTICHAIN_DEPLOY_RESULT!'); + + const rawResult = JSON.stringify(finalResults); + // Эту строку парсят unifiedDeploymentService и dleV2Service по шаблону /MULTICHAIN_DEPLOY_RESULT\\s+(.+)/ + process.stdout.write(`MULTICHAIN_DEPLOY_RESULT ${rawResult}\n`); + + logger.info('[MULTI_DBG] ✅ MULTICHAIN_DEPLOY_RESULT ВЫВЕДЕН!'); logger.info('[MULTI_DBG] DLE deployment completed successfully with integrated verification!'); } @@ -845,7 +833,9 @@ main().catch((e) => { stack: e.stack }; - console.log('MULTICHAIN_DEPLOY_RESULT', JSON.stringify([errorResult])); + // Даже в случае ошибки выводим сырой результат без маскирования + const rawError = JSON.stringify([errorResult]); + process.stdout.write(`MULTICHAIN_DEPLOY_RESULT ${rawError}\n`); process.exit(1); }); diff --git a/frontend/src/views/smartcontracts/AddModuleFormView.vue b/frontend/src/views/smartcontracts/AddModuleFormView.vue index 05980df..307bfff 100644 --- a/frontend/src/views/smartcontracts/AddModuleFormView.vue +++ b/frontend/src/views/smartcontracts/AddModuleFormView.vue @@ -446,7 +446,7 @@ const loadDleData = async () => { console.log('Начинаем загрузку данных DLE для адреса:', dleAddress.value); isLoadingDle.value = true; try { - const response = await api.post('/dle-core/read-dle-info', { + const response = await api.post('/blockchain/read-dle-info', { dleAddress: dleAddress.value }); diff --git a/frontend/src/views/smartcontracts/AnalyticsView.vue b/frontend/src/views/smartcontracts/AnalyticsView.vue index caf0efe..867dcaf 100644 --- a/frontend/src/views/smartcontracts/AnalyticsView.vue +++ b/frontend/src/views/smartcontracts/AnalyticsView.vue @@ -311,7 +311,7 @@ async function loadDleData() { console.log('[AnalyticsView] Загрузка данных DLE:', dleAddress.value); // Читаем данные из блокчейна - const response = await api.post('/dle-core/read-dle-info', { + const response = await api.post('/blockchain/read-dle-info', { dleAddress: dleAddress.value }); diff --git a/frontend/src/views/smartcontracts/CreateProposalView.vue b/frontend/src/views/smartcontracts/CreateProposalView.vue index c6ed32f..88a956d 100644 --- a/frontend/src/views/smartcontracts/CreateProposalView.vue +++ b/frontend/src/views/smartcontracts/CreateProposalView.vue @@ -309,7 +309,7 @@ async function loadDleData() { isLoadingDle.value = true; try { // Загружаем данные DLE из блокчейна - const response = await api.post('/dle-core/read-dle-info', { + const response = await api.post('/blockchain/read-dle-info', { dleAddress: dleAddress.value }); diff --git a/frontend/src/views/smartcontracts/HistoryView.vue b/frontend/src/views/smartcontracts/HistoryView.vue index 7bf8b6e..b38bec4 100644 --- a/frontend/src/views/smartcontracts/HistoryView.vue +++ b/frontend/src/views/smartcontracts/HistoryView.vue @@ -325,7 +325,7 @@ async function loadDleData() { console.log('[HistoryView] Загрузка данных DLE:', dleAddress.value); // Читаем данные из блокчейна - const response = await api.post('/dle-core/read-dle-info', { + const response = await api.post('/blockchain/read-dle-info', { dleAddress: dleAddress.value });