diff --git a/.gitignore b/.gitignore index 4560272980..bf565453a5 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ website/translations.json website/src/img/images/ website/src/images/ website/src/js/lottie.min.js +website/src/js/ethers* website/src/privacy.md # Generated files website/package/generated* diff --git a/README.md b/README.md index 4aa0860af1..f2641cd06d 100644 --- a/README.md +++ b/README.md @@ -425,6 +425,8 @@ Please do NOT report security vulnerabilities via GitHub issues. This software is licensed under the GNU Affero General Public License version 3 (AGPLv3). See the [LICENSE](./LICENSE) file for details. The SimpleX and SimpleX Chat name, logo, and associated branding materials are not covered by this license and are subject to the terms outlined in the [TRADEMARK](./docs/TRADEMARK.md) file. +Graphic designs, artworks and layouts are not licensed for re-use. If you want to use them in your publications, please ask for permission. Texts can be used as direct quotes, referencing the source. + [iOS app](https://apps.apple.com/us/app/simplex-chat/id1605771084)   [![Android app](https://github.com/simplex-chat/.github/blob/master/profile/images/google_play.svg)](https://play.google.com/store/apps/details?id=chat.simplex.app) diff --git a/apps/ios/Shared/Theme/Theme.swift b/apps/ios/Shared/Theme/Theme.swift index de67390026..3bd8f00c25 100644 --- a/apps/ios/Shared/Theme/Theme.swift +++ b/apps/ios/Shared/Theme/Theme.swift @@ -42,12 +42,14 @@ class AppTheme: ObservableObject, Equatable { } func updateFromCurrentColors() { - objectWillChange.send() - name = CurrentColors.name - base = CurrentColors.base - colors.updateColorsFrom(CurrentColors.colors) - appColors.updateColorsFrom(CurrentColors.appColors) - wallpaper.updateWallpaperFrom(CurrentColors.wallpaper) + DispatchQueue.main.async { + self.objectWillChange.send() + self.name = CurrentColors.name + self.base = CurrentColors.base + self.colors.updateColorsFrom(CurrentColors.colors) + self.appColors.updateColorsFrom(CurrentColors.appColors) + self.wallpaper.updateWallpaperFrom(CurrentColors.wallpaper) + } } } diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff index d13492c4e1..22d223ffd9 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff @@ -272,7 +272,7 @@ %lldd - %lldn + %lldnap No comment provided by engineer. @@ -292,7 +292,7 @@ %lldmth - %lldh + %lldhónap No comment provided by engineer. @@ -1199,17 +1199,17 @@ swipe action Bad desktop address - Érvénytelen számítógépcím + Hibás a számítógép címe No comment provided by engineer. Bad message ID - Téves üzenet ID + Hibás az üzenet azonosítója No comment provided by engineer. Bad message hash - Érvénytelen az üzenet kivonata + Hibás az üzenet kivonata No comment provided by engineer. @@ -1239,7 +1239,7 @@ swipe action Better networking - Jobb hálózatkezelés + Továbbfejlesztett hálózatkezelés No comment provided by engineer. @@ -2267,7 +2267,7 @@ Ez a saját egyszer használható meghívója! Current conditions text couldn't be loaded, you can review conditions via this link: - A jelenlegi feltételek szövegét nem lehetett betölteni, a feltételeket a következő hivatkozáson keresztül vizsgálhatja felül: + A jelenlegi feltételek szövegét nem sikerült betölteni, a feltételeket a következő hivatkozáson keresztül vizsgálhatja felül: No comment provided by engineer. @@ -2771,7 +2771,7 @@ swipe action Different names, avatars and transport isolation. - Különböző nevek, profilképek és átvitelizoláció. + Különböző nevek, profilképek és átvitelelkülönítés. No comment provided by engineer. @@ -3457,7 +3457,7 @@ chat item action Error opening group - Hiba a csoport előkészítésekor + Hiba történt a csoport megnyitásakor No comment provided by engineer. @@ -3843,7 +3843,7 @@ snd error text Files and media not allowed - A fájlok és a médiatartalmak nincsenek engedélyezve + A fájlok és a médiatartalmak küldése nincs engedélyezve No comment provided by engineer. @@ -4362,7 +4362,7 @@ Hiba: %2$@ Import failed - Sikertelen importálás + Nem sikerült az importálás No comment provided by engineer. @@ -4389,12 +4389,12 @@ További fejlesztések hamarosan! Improved privacy and security - Fejlesztett adatvédelem és biztonság + Továbbfejlesztett adatvédelem és biztonság No comment provided by engineer. Improved server configuration - Javított kiszolgáló konfiguráció + Továbbfejlesztett kiszolgálókonfiguráció No comment provided by engineer. @@ -4672,7 +4672,7 @@ További fejlesztések hamarosan! Join as %@ - csatlakozás mint %@ + Csatlakozás mint: %@ No comment provided by engineer. @@ -5574,7 +5574,7 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz! No servers to send files. - Nincsenek fájlküldő-kiszolgálók. + Nincsenek fájlküldési kiszolgálók. servers error @@ -5784,7 +5784,7 @@ VPN engedélyezése szükséges. Only your contact can send files and media. - Csak a partnere küldhet fájlokat és a médiatartalmakat. + Csak a partnere küldhet fájlokat és médiatartalmakat. No comment provided by engineer. @@ -7536,7 +7536,7 @@ chat item action Share old link - Teljes hivatkozás megosztása + Régi (hosszú) hivatkozás megosztása alert button @@ -8595,7 +8595,7 @@ A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcso Unlink - Szétkapcsolás + Leválasztás No comment provided by engineer. @@ -8755,7 +8755,7 @@ A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcso Use TCP port 443 for preset servers only. - A 443-as TCP-port használata kizárólag az előre beállított kiszolgálokhoz. + A 443-as TCP-port használata kizárólag az előre beállított kiszolgálókhoz. No comment provided by engineer. @@ -8815,7 +8815,7 @@ A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcso Use private routing with unknown servers. - Privát útválasztás használata ismeretlen kiszolgálókkal. + Privát útválasztás használata az ismeretlen kiszolgálókhoz. No comment provided by engineer. @@ -9317,7 +9317,7 @@ Megismétli a csatlakozási kérést? You can't send messages! - Nem lehet üzeneteket küldeni! + Ön nem tud üzeneteket küldeni! alert title @@ -9529,7 +9529,7 @@ Megismétli a kapcsolódási kérést? Your credentials may be sent unencrypted. - A hitelesítési adati titkosítatlanul is elküldhetők. + A hitelesítési adatai titkosítatlanul is elküldhetők. No comment provided by engineer. @@ -9559,7 +9559,7 @@ Megismétli a kapcsolódási kérést? Your profile - Profil + Saját profil No comment provided by engineer. @@ -10523,7 +10523,7 @@ utoljára fogadott üzenet: %2$@ unknown servers - ismeretlen átjátszók + ismeretlen kiszolgálók No comment provided by engineer. diff --git a/apps/ios/SimpleX--iOS--Info.plist b/apps/ios/SimpleX--iOS--Info.plist index 6f7d6c2395..72bd9b0dc3 100644 --- a/apps/ios/SimpleX--iOS--Info.plist +++ b/apps/ios/SimpleX--iOS--Info.plist @@ -56,5 +56,7 @@ remote-notification voip + UIDesignRequiresCompatibility + diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index b3ec79d125..930d86441b 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -183,8 +183,8 @@ 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; }; 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; }; - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC-ghc9.6.3.a */; }; - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN.a */; }; 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; }; 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; }; 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; }; @@ -555,8 +555,8 @@ 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; 64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC-ghc9.6.3.a"; sourceTree = ""; }; - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC.a"; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN.a"; sourceTree = ""; }; 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; }; 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = ""; }; @@ -718,8 +718,8 @@ 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC-ghc9.6.3.a in Frameworks */, - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -805,8 +805,8 @@ 64C829992D54AEEE006B9E89 /* libffi.a */, 64C829982D54AEED006B9E89 /* libgmp.a */, 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC-ghc9.6.3.a */, - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.2-6qgCrd2M1igHbyytG3wCNC.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.0.3-8ATMbsLjvgc8dH25M4OGyN.a */, ); path = Libraries; sourceTree = ""; @@ -2013,7 +2013,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2038,7 +2038,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES_THIN; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; @@ -2063,7 +2063,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2088,7 +2088,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; @@ -2105,11 +2105,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2125,11 +2125,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2150,7 +2150,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = s; @@ -2165,7 +2165,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2187,7 +2187,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_CODE_COVERAGE = NO; @@ -2202,7 +2202,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2224,7 +2224,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2250,7 +2250,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2275,7 +2275,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2301,7 +2301,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2326,7 +2326,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2341,7 +2341,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2360,7 +2360,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 309; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2375,7 +2375,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.4.6; + MARKETING_VERSION = 6.5; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; diff --git a/apps/ios/hu.lproj/Localizable.strings b/apps/ios/hu.lproj/Localizable.strings index 08ebb0415b..451bdfc699 100644 --- a/apps/ios/hu.lproj/Localizable.strings +++ b/apps/ios/hu.lproj/Localizable.strings @@ -227,7 +227,7 @@ "%lld seconds" = "%lld mp"; /* No comment provided by engineer. */ -"%lldd" = "%lldn"; +"%lldd" = "%lldnap"; /* No comment provided by engineer. */ "%lldh" = "%lldó"; @@ -239,7 +239,7 @@ "%lldm" = "%lldp"; /* No comment provided by engineer. */ -"%lldmth" = "%lldh"; +"%lldmth" = "%lldhónap"; /* No comment provided by engineer. */ "%llds" = "%lldmp"; @@ -774,19 +774,19 @@ swipe action */ "Background" = "Háttér"; /* No comment provided by engineer. */ -"Bad desktop address" = "Érvénytelen számítógépcím"; +"Bad desktop address" = "Hibás a számítógép címe"; /* integrity error chat item */ "bad message hash" = "hibás az üzenet kivonata"; /* No comment provided by engineer. */ -"Bad message hash" = "Érvénytelen az üzenet kivonata"; +"Bad message hash" = "Hibás az üzenet kivonata"; /* integrity error chat item */ "bad message ID" = "hibás az üzenet azonosítója"; /* No comment provided by engineer. */ -"Bad message ID" = "Téves üzenet ID"; +"Bad message ID" = "Hibás az üzenet azonosítója"; /* No comment provided by engineer. */ "Better calls" = "Továbbfejlesztett hívásélmény"; @@ -804,7 +804,7 @@ swipe action */ "Better messages" = "Továbbfejlesztett üzenetek"; /* No comment provided by engineer. */ -"Better networking" = "Jobb hálózatkezelés"; +"Better networking" = "Továbbfejlesztett hálózatkezelés"; /* No comment provided by engineer. */ "Better notifications" = "Továbbfejlesztett értesítések"; @@ -1522,7 +1522,7 @@ set passcode view */ "creator" = "készítő"; /* No comment provided by engineer. */ -"Current conditions text couldn't be loaded, you can review conditions via this link:" = "A jelenlegi feltételek szövegét nem lehetett betölteni, a feltételeket a következő hivatkozáson keresztül vizsgálhatja felül:"; +"Current conditions text couldn't be loaded, you can review conditions via this link:" = "A jelenlegi feltételek szövegét nem sikerült betölteni, a feltételeket a következő hivatkozáson keresztül vizsgálhatja felül:"; /* No comment provided by engineer. */ "Current Passcode" = "Jelenlegi jelkód"; @@ -1857,7 +1857,7 @@ swipe action */ "different migration in the app/database: %@ / %@" = "különböző átköltöztetés az alkalmazásban/adatbázisban: %@ / %@"; /* No comment provided by engineer. */ -"Different names, avatars and transport isolation." = "Különböző nevek, profilképek és átvitelizoláció."; +"Different names, avatars and transport isolation." = "Különböző nevek, profilképek és átvitelelkülönítés."; /* connection level description */ "direct" = "közvetlen"; @@ -2326,7 +2326,7 @@ chat item action */ "Error opening chat" = "Hiba történt a csevegés megnyitásakor"; /* No comment provided by engineer. */ -"Error opening group" = "Hiba a csoport előkészítésekor"; +"Error opening group" = "Hiba történt a csoport megnyitásakor"; /* alert title */ "Error receiving file" = "Hiba történt a fájl fogadásakor"; @@ -2559,7 +2559,7 @@ snd error text */ "Files and media are prohibited." = "A fájlok és a médiatartalmak küldése le van tiltva."; /* No comment provided by engineer. */ -"Files and media not allowed" = "A fájlok és a médiatartalmak nincsenek engedélyezve"; +"Files and media not allowed" = "A fájlok és a médiatartalmak küldése nincs engedélyezve"; /* No comment provided by engineer. */ "Files and media prohibited!" = "A fájlok és a médiatartalmak küldése le van tiltva!"; @@ -2883,7 +2883,7 @@ snd error text */ "Import database" = "Adatbázis importálása"; /* No comment provided by engineer. */ -"Import failed" = "Sikertelen importálás"; +"Import failed" = "Nem sikerült az importálás"; /* No comment provided by engineer. */ "Import theme" = "Téma importálása"; @@ -2898,10 +2898,10 @@ snd error text */ "Improved message delivery" = "Továbbfejlesztett üzenetkézbesítés"; /* No comment provided by engineer. */ -"Improved privacy and security" = "Fejlesztett adatvédelem és biztonság"; +"Improved privacy and security" = "Továbbfejlesztett adatvédelem és biztonság"; /* No comment provided by engineer. */ -"Improved server configuration" = "Javított kiszolgáló konfiguráció"; +"Improved server configuration" = "Továbbfejlesztett kiszolgálókonfiguráció"; /* No comment provided by engineer. */ "In order to continue, chat should be stopped." = "A folytatáshoz a csevegést meg kell szakítani."; @@ -3117,7 +3117,7 @@ snd error text */ "Join" = "Csatlakozás"; /* No comment provided by engineer. */ -"Join as %@" = "csatlakozás mint %@"; +"Join as %@" = "Csatlakozás mint: %@"; /* new chat sheet title */ "Join group" = "Csatlakozás a csoporthoz"; @@ -3711,7 +3711,7 @@ snd error text */ "No servers to receive messages." = "Nincsenek üzenetfogadási kiszolgálók."; /* servers error */ -"No servers to send files." = "Nincsenek fájlküldő-kiszolgálók."; +"No servers to send files." = "Nincsenek fájlküldési kiszolgálók."; /* No comment provided by engineer. */ "no subscription" = "nincs előfizetés"; @@ -3860,7 +3860,7 @@ new chat action */ "Only your contact can send disappearing messages." = "Csak a partnere tud eltűnő üzeneteket küldeni."; /* No comment provided by engineer. */ -"Only your contact can send files and media." = "Csak a partnere küldhet fájlokat és a médiatartalmakat."; +"Only your contact can send files and media." = "Csak a partnere küldhet fájlokat és médiatartalmakat."; /* No comment provided by engineer. */ "Only your contact can send voice messages." = "Csak a partnere tud hangüzeneteket küldeni."; @@ -5007,7 +5007,7 @@ chat item action */ "Share old address" = "Régi cím megosztása"; /* alert button */ -"Share old link" = "Teljes hivatkozás megosztása"; +"Share old link" = "Régi (hosszú) hivatkozás megosztása"; /* No comment provided by engineer. */ "Share profile" = "Profil megosztása"; @@ -5656,7 +5656,7 @@ report reason */ "Unknown error" = "Ismeretlen hiba"; /* No comment provided by engineer. */ -"unknown servers" = "ismeretlen átjátszók"; +"unknown servers" = "ismeretlen kiszolgálók"; /* alert title */ "Unknown servers!" = "Ismeretlen kiszolgálók!"; @@ -5671,7 +5671,7 @@ report reason */ "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Hacsak a partnere nem törölte a kapcsolatot, vagy ez a hivatkozás már használatban volt egyszer, lehet hogy ez egy hiba – jelentse a problémát.\nA kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcsolattartási hivatkozást, és ellenőrizze, hogy a hálózati kapcsolat stabil-e."; /* No comment provided by engineer. */ -"Unlink" = "Szétkapcsolás"; +"Unlink" = "Leválasztás"; /* No comment provided by engineer. */ "Unlink desktop?" = "Leválasztja a számítógépet?"; @@ -5800,7 +5800,7 @@ report reason */ "Use private routing with unknown servers when IP address is not protected." = "Privát útválasztás használata az ismeretlen kiszolgálókkal, ha az IP-cím nem védett."; /* No comment provided by engineer. */ -"Use private routing with unknown servers." = "Privát útválasztás használata ismeretlen kiszolgálókkal."; +"Use private routing with unknown servers." = "Privát útválasztás használata az ismeretlen kiszolgálókhoz."; /* No comment provided by engineer. */ "Use server" = "Kiszolgáló használata"; @@ -5818,7 +5818,7 @@ report reason */ "Use TCP port %@ when no port is specified." = "A következő TCP-port használata, amikor nincs port megadva: %@."; /* No comment provided by engineer. */ -"Use TCP port 443 for preset servers only." = "A 443-as TCP-port használata kizárólag az előre beállított kiszolgálokhoz."; +"Use TCP port 443 for preset servers only." = "A 443-as TCP-port használata kizárólag az előre beállított kiszolgálókhoz."; /* No comment provided by engineer. */ "Use the app while in the call." = "Alkalmazás használata hívás közben."; @@ -6166,7 +6166,7 @@ report reason */ "You can view your reports in Chat with admins." = "A jelentéseket megtekintheti a „Csevegés az adminisztrátorokkal” menüben."; /* alert title */ -"You can't send messages!" = "Nem lehet üzeneteket küldeni!"; +"You can't send messages!" = "Ön nem tud üzeneteket küldeni!"; /* chat item text */ "you changed address" = "Ön módosította a címet"; @@ -6316,7 +6316,7 @@ report reason */ "Your contacts will remain connected." = "A partnerei továbbra is kapcsolódva maradnak."; /* No comment provided by engineer. */ -"Your credentials may be sent unencrypted." = "A hitelesítési adati titkosítatlanul is elküldhetők."; +"Your credentials may be sent unencrypted." = "A hitelesítési adatai titkosítatlanul is elküldhetők."; /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "A jelenlegi csevegési adatbázis TÖRÖLVE és CSERÉLVE lesz az importáltra."; @@ -6337,7 +6337,7 @@ report reason */ "Your privacy" = "Adatvédelem"; /* No comment provided by engineer. */ -"Your profile" = "Profil"; +"Your profile" = "Saját profil"; /* No comment provided by engineer. */ "Your profile **%@** will be shared." = "A(z) **%@** nevű profilja meg lesz osztva."; diff --git a/apps/multiplatform/android/build.gradle.kts b/apps/multiplatform/android/build.gradle.kts index 8168c92bf8..9b4e7ad58c 100644 --- a/apps/multiplatform/android/build.gradle.kts +++ b/apps/multiplatform/android/build.gradle.kts @@ -15,8 +15,7 @@ android { applicationId = "chat.simplex.app" namespace = "chat.simplex.app" minSdk = 26 - //noinspection OldTargetApi - targetSdk = 34 + targetSdk = 35 // !!! // skip version code after release to F-Droid, as it uses two version codes versionCode = (extra["android.version_code"] as String).toInt() diff --git a/apps/multiplatform/common/build.gradle.kts b/apps/multiplatform/common/build.gradle.kts index 6b0c0f441e..602b8f2b90 100644 --- a/apps/multiplatform/common/build.gradle.kts +++ b/apps/multiplatform/common/build.gradle.kts @@ -95,7 +95,8 @@ kotlin { implementation("com.jakewharton:process-phoenix:3.0.0") - val cameraXVersion = "1.3.4" + // https://issuetracker.google.com/issues/351313880 + val cameraXVersion = "1.5.1" implementation("androidx.camera:camera-core:${cameraXVersion}") implementation("androidx.camera:camera-camera2:${cameraXVersion}") implementation("androidx.camera:camera-lifecycle:${cameraXVersion}") diff --git a/apps/multiplatform/common/src/commonMain/cpp/android/CMakeLists.txt b/apps/multiplatform/common/src/commonMain/cpp/android/CMakeLists.txt index 44cb31d424..49794a8ab5 100644 --- a/apps/multiplatform/common/src/commonMain/cpp/android/CMakeLists.txt +++ b/apps/multiplatform/common/src/commonMain/cpp/android/CMakeLists.txt @@ -57,6 +57,9 @@ set_target_properties( support PROPERTIES IMPORTED_LOCATION # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. +# https://developer.android.com/guide/practices/page-sizes#cmake +target_link_options(app-lib PRIVATE "-Wl,-z,max-page-size=16384") + target_link_libraries( # Specifies the target library. app-lib diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml index b1d27b79a5..41f454b2dd 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml @@ -1128,7 +1128,7 @@ معرف الرسالة التالية غير صحيح (أقل أو يساوي السابق). \nيمكن أن يحدث ذلك بسبب بعض العلل أو عندما يُخترق الاتصال. أزل من المفضلة - محاولة الاتصال بالخادم المستخدم لاستلام الرسائل من جهة الاتصال هذه. + محاولة الاتصال بالخادم المستخدم لاستلام الرسائل من هذا الاتصال. اختيار ملف إرسال غير مصرح به محاولة الاتصال بالخادم المستخدم لاستلام الرسائل من جهة الاتصال هذه (خطأ: %1$s). @@ -1137,7 +1137,7 @@ أنت تستخدم ملف تعريف متخفي لهذه المجموعة - لمنع مشاركة ملفك التعريفي الرئيسي الذي يدعو جهات الاتصال غير مسموح به غيّرتَ دور %s إلى %s نعم - أنت متصل بالخادم المستخدم لاستلام الرسائل من جهة الاتصال هذه. + أنت متصل بالخادم المستخدم لاستلام الرسائل من هذا الاتصال. أنت لقد شاركت رابط لمرة واحدة سيتم إرسال ملف التعريفك إلى جهة الاتصال التي استلمت منها هذا الرابط. @@ -2520,4 +2520,6 @@ البصمة في عنوان الخادم الوجهة لا تتطابق مع الشهادة: %1$s. البصمة في عنوان خادم التحويل لا تتطابق مع الشهادة: %1$s. البصمة في عنوان الخادم لا تتطابق مع الشهادة: %1$s. + لا اشتراك + أنت غير متصل بالخادم المستخدم لاستقبال الرسائل من هذا الاتصال (لا يوجد اشتراك). diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml index 4162120ef7..12b578edbf 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml @@ -429,7 +429,7 @@ Leírás %d óra %dp - Szétkapcsolás + Kapcsolat bontása Szerkesztés Letiltás (csoport egyéni beállításainak megtartása) %d csoportesemény @@ -532,7 +532,7 @@ Fájlok és médiatartalmak KONZOLHOZ Nem sikerült a titkosítást újraegyeztetni. - Hiba történt a felhasználóprofil törlésekor + Hiba történt a felhasználói profil törlésekor Csoporttag általi javítás nem támogatott Adja meg az üdvözlőüzenetet… Titkosított adatbázis @@ -634,7 +634,7 @@ Hogyan Összecsukás Kép - Fejlesztett adatvédelem és biztonság + Továbbfejlesztett adatvédelem és biztonság Mellőzés Kép elküldve Se név, se üzenet @@ -645,7 +645,7 @@ Inkognitó Használati útmutató Alkalmazás képernyőjének elrejtése a gyakran használt alkalmazások között. - Javított kiszolgáló konfiguráció + Továbbfejlesztett kiszolgálókonfiguráció Előzmények Rejtett profiljelszó Adatbázis importálása @@ -751,7 +751,7 @@ bekapcsolva Japán és portugál kezelőfelület Az üzenetek végleges törlése le van tiltva. - %s nevű hordozható eszközzel]]> + %s nevű hordozható eszköz le lett választva]]> hónap Üzenetvázlat Egy üzenet eltüntetése @@ -817,7 +817,7 @@ Barátok meghívása Menük és figyelmeztetések Tagok meghívása - Csatlakozás mint %s + Csatlakozás mint: %s Nincs csevegés kijelölve Csak helyi profiladatok inkognitó egy egyszer használható meghívón keresztül @@ -1142,7 +1142,7 @@ Ez a QR-kód nem egy hivatkozás! Várakozás a fájlra simplexmq: v%s (%2s) - Szétkapcsolás + Leválasztás Véletlenszerű profil Érvénytelen jelmondat! A reakciók hozzáadása az üzenetekhez le van tiltva. @@ -1269,7 +1269,7 @@ Ötletek és javaslatok Figyelmeztetés: néhány adat elveszhet! Koppintson ide az új csevegés indításához - Várakozás a számítógépre… + Várakozás a számítógép-alkalmazásra… Az üzenetváltás jövője Módosítja a hálózati beállításokat? Várakozás a hordozható eszköz társítására: @@ -1520,7 +1520,7 @@ Számítógép inaktív Csevegés újraindítása Időtúllépés a számítógéphez való csatlakozáskor - Kapcsolat bontva a számítógéppel + A számítógép le lett választva A kapcsolat megszakadt A kapcsolat megszakadt A kapcsolat a számítógéppel rossz állapotban van @@ -1528,10 +1528,10 @@ Jelentse a fejlesztőknek: \n%s %s hordozható eszköz által használt alkalmazás verziója nem támogatott. Győződjön meg arról, hogy mindkét eszközön ugyanazt a verziót használja]]> - %s nevű hordozható eszközzel]]> + %s nevű hordozható eszköz le lett választva]]> Érvénytelen megjelenítendő név! Ez a megjelenítendő név érvénytelen. Válasszon egy másik nevet. - %s nevű hordozható eszközzel, a következő okból: %s]]> + %s nevű hordozható eszköz le lett választva, a következő okból: %s]]> Kapcsolat bontva a következő okból: %s %s hordozható eszköz nem található]]> %s hordozható eszközzel rossz állapotban van]]> @@ -1741,7 +1741,7 @@ Igen NE használjon privát útválasztást. Privát útválasztás - Privát útválasztás használata az ismeretlen kiszolgálókkal. + Privát útválasztás használata az ismeretlen kiszolgálókhoz. Mindig használjon privát útválasztást. Üzenet-útválasztási mód Közvetlen üzenetküldés, ha az IP-cím védett és a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. @@ -1790,7 +1790,7 @@ Perzsa kezelőfelület Védje az IP-címét a partnerei által kiválasztott üzenetváltási továbbítókiszolgálókkal szemben.\nEngedélyezze a *Hálózat és kiszolgálók* menüben. Ismeretlen kiszolgálókról származó fájlok megerősítése. - Javított üzenetkézbesítés + Továbbfejlesztett üzenetkézbesítés Alkalmazás témájának visszaállítása Tegye egyedivé a csevegéseit! Új csevegési témák @@ -2057,7 +2057,7 @@ Üzenetbuborék alakja Farok Kiszolgáló - Minden alkalommal, amikor elindítja az alkalmazást, új SOCKS-hitelesítési adatokat fog használni. + Minden alkalommal, amikor elindítja az alkalmazást, új SOCKS-hitelesítési adatok lesznek használva. Alkalmazás munkamenete Az összes kiszolgálóhoz új, SOCKS-hitelesítési adatok lesznek használva. Kattintson a címmező melletti info gombra a mikrofon használatának engedélyezéséhez. @@ -2127,7 +2127,7 @@ Hálózatüzemeltető Weboldal Feltételek elfogadásának ideje: %s. - A feltételek el lesznek elfogadva a következő időpontban: %s. + A feltételek el lesznek fogadva a következő időpontban: %s. Kiszolgálók használata %s használata A jelenlegi feltételek szövegét nem sikerült betölteni, a feltételeket a következő hivatkozáson keresztül vizsgálhatja felül: @@ -2162,7 +2162,7 @@ A Flux kiszolgálókat engedélyezheti a beállításokban, a „Hálózat és kiszolgálók” menüben, a metaadatok jobb védelme érdekében. Eszköztárak a metaadatok jobb védelme érdekében. - Javított csevegési navigáció + Továbbfejlesztett csevegési navigáció - Csevegés megnyitása az első olvasatlan üzenetnél.\n- Ugrás az idézett üzenetekre. Frissített feltételek megtekintése A jelenlegi csevegési profiljához tartozó új fájlok kiszolgálói @@ -2352,7 +2352,7 @@ Összes kiszolgáló Kikapcsolva Előre beállított kiszolgálók - A 443-as TCP-port használata kizárólag az előre beállított kiszolgálokhoz. + A 443-as TCP-port használata kizárólag az előre beállított kiszolgálókhoz. Hiba a tag befogadásakor %d csevegés a tagokkal %d üzenet @@ -2398,7 +2398,7 @@ a partner nem áll készen nincs szinkronizálva Törli a taggal való csevegést? - a partnere elhagyta a csevegést + partner törölve Csevegés törlése Elutasítás Elutasítja a tagot? @@ -2408,8 +2408,8 @@ Új csoport megnyitása végpontok közötti titkosítással vannak védve.]]> Hiba történt a partneri kapcsolatkérés elutasításakor - Hiba a csevegés megnyitásakor - Hiba a csoport megnyitásakor + Hiba történt a csevegés megnyitásakor + Hiba történt a csoport megnyitásakor Hiba a profil módosításakor Megnyitás a csatlakozáshoz Megnyitás a kapcsolódáshoz @@ -2474,11 +2474,11 @@ Cím frissítése Üdvözölje a partnereit 👋 4 új kezelőfelületi nyelv - Katalán, indonéz, román és vietnami – köszönjük felhasználóinknak! + Katalán, indonéz, román és vietnámi – köszönjük a felhasználóinknak! Csoporthivatkozás frissítése A hivatkozás rövid lesz és a csoportprofil meg lesz osztva a hivatkozáson keresztül. Régi cím megosztása - Régi hivatkozás megosztása + Régi (hosszú) hivatkozás megosztása PARTNERI KAPCSOLATKÉRÉSEK A CSOPORTOKBÓL A tag törölve lett – nem lehet elfogadni a kérést a(z) %1$s nevű csoportból partneri kapcsolatot kért diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml index 15641be96f..1c7e39d51e 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml @@ -2014,7 +2014,7 @@ connetti Contatto eliminato! Confermare l\'eliminazione del contatto? - Messaggio + Chatta Nessuna selezione Seleziona Selezionato %d diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml index 8a444d6c2b..f9a1a5b131 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -2125,4 +2125,7 @@ 簡化的匿名模式 點擊以連接 從桌面使用 + 開啓新聊天 + 接受聯絡請求 + 接受聯絡請求 diff --git a/apps/multiplatform/gradle.properties b/apps/multiplatform/gradle.properties index 53e4c60872..ced522ce5e 100644 --- a/apps/multiplatform/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -24,13 +24,13 @@ android.nonTransitiveRClass=true kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.jvm.target=11 -android.version_name=6.4.6 -android.version_code=321 +android.version_name=6.5-beta.0 +android.version_code=323 android.bundle=false -desktop.version_name=6.4.6 -desktop.version_code=121 +desktop.version_name=6.5-beta.0 +desktop.version_code=122 kotlin.version=2.1.20 gradle.plugin.version=8.7.0 diff --git a/cabal.project b/cabal.project index 46d8a43e55..0799c5255c 100644 --- a/cabal.project +++ b/cabal.project @@ -48,7 +48,7 @@ source-repository-package source-repository-package type: git location: https://github.com/simplex-chat/zip.git - tag: bd421c6b19cc4c465cd7af1f6f26169fb8ee1ebc + tag: 2eff156c3aac389e35d38bf10a52733d7061640a -- waiting for published warp-tls-3.4.7 source-repository-package diff --git a/docs/GLOSSARY.md b/docs/GLOSSARY.md index 0cb855d729..fe0bd107ed 100644 --- a/docs/GLOSSARY.md +++ b/docs/GLOSSARY.md @@ -4,6 +4,10 @@ Choosing a private messenger requires the understanding of many technical terms, While this glossary aims to be factual and objective, it is not completely unbiased. We designed SimpleX to be the most private, secure and resilient communication network, and some definitions reflect this view. +## 2-factor key exchange + +The ability of communication service to ensure the security of the [key agreement protocol](#key-agreement-protocol) against [man-in-the-middle](#man-in-the-middle-attack). + ## Address portability Similarly to [phone number portability](https://en.wikipedia.org/wiki/Local_number_portability) (the ability of the customer to transfer the service to another provider without changing the number), the address portability means the ability of a communication service customer to change the service provider without changing the service address. Many [federated networks](#federated-network) support SRV records to provide address portability, but allowing service users to set up their own domains for the addresses is not as commonly supported by the available server and client software as for email. @@ -89,7 +93,7 @@ Also known as perfect forward secrecy, it is a feature of a [key agreement proto ## Key agreement protocol -Also known as key exchange, it is a process of agreeing cryptographic keys between the sender and the recipient(s) of the message. It is required for [end-to-end encryption](#end-to-end-encryption) to work. +Also known as key exchange, it is a process of agreeing cryptographic keys between the sender and the recipient(s) of the message. It is required for [end-to-end encryption](#end-to-end-encryption) to work. Unless it is possible to secure the key exchange via [some second factor](#2-factor-key-exchange), e.g. security code verification, it can be vulnerable to [man-in-the-middle attack](#man-in-the-middle-attack). [Wikipedia](https://en.wikipedia.org/wiki/Key-agreement_protocol) @@ -169,11 +173,11 @@ The advantage is that the participants do not depend on any servers. There are [ ## Post-compromise security -Also known as break-in recovery, it is the quality of the end-to-end encryption scheme allowing to recover security against a passive attacker who observes encrypted messages after compromising one (or both) of the parties. Also known as recovery from compromise or break-in recovery. [Double-ratchet algorithm](#double-ratchet-algorithm) has this quality. +The quality of the end-to-end encryption scheme allowing to recover security against a passive attacker who observes encrypted messages after compromising one (or both) of the parties. Also known as recovery from compromise or break-in recovery. [Double-ratchet algorithm](#double-ratchet-algorithm) has this quality. ## Post-quantum cryptography -Any of the proposed cryptographic systems or algorithms that are thought to be secure against an attack by a quantum computer. It appears that as of 2023 there is no system or algorithm that is proven to be secure against such attacks, or even to be secure against attacks by massively parallel conventional computers, so a general recommendation is to use post-quantum cryptographic systems in combination with the traditional cryptographic systems. +Any of the proposed cryptographic systems or algorithms that are thought to be secure against an attack by a quantum computer. It appears that as of 2025 there is no system or algorithm that is *proven* to be secure against such attacks, or even to be secure against attacks by massively parallel conventional computers, so a general recommendation is to use post-quantum hybrid cryptography - combining post-quantum and traditional algorigthms. [Wikipedia](https://en.wikipedia.org/wiki/Post-quantum_cryptography) diff --git a/eth/nft/.gitignore b/eth/nft/.gitignore new file mode 100644 index 0000000000..d751a74728 --- /dev/null +++ b/eth/nft/.gitignore @@ -0,0 +1,2 @@ +.deps +artifacts/ diff --git a/eth/nft/.prettierrc.json b/eth/nft/.prettierrc.json new file mode 100644 index 0000000000..b2a56f2371 --- /dev/null +++ b/eth/nft/.prettierrc.json @@ -0,0 +1,38 @@ +{ + "overrides": [ + { + "files": "*.sol", + "options": { + "printWidth": 80, + "tabWidth": 4, + "useTabs": false, + "singleQuote": false, + "bracketSpacing": false + } + }, + { + "files": "*.yml", + "options": {} + }, + { + "files": "*.yaml", + "options": {} + }, + { + "files": "*.toml", + "options": {} + }, + { + "files": "*.json", + "options": {} + }, + { + "files": "*.js", + "options": {} + }, + { + "files": "*.ts", + "options": {} + } + ] +} diff --git a/eth/nft/compiler_config.json b/eth/nft/compiler_config.json new file mode 100644 index 0000000000..9026d3afa9 --- /dev/null +++ b/eth/nft/compiler_config.json @@ -0,0 +1,16 @@ + +{ + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": true, + "runs": 200 + }, + "outputSelection": { + "*": { + "": ["ast"], + "*": ["abi", "metadata", "devdoc", "userdoc", "storageLayout", "evm.legacyAssembly", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers", "evm.gasEstimates", "evm.assembly"] + } + } + } +} diff --git a/eth/nft/contracts/MultiERC1155.sol b/eth/nft/contracts/MultiERC1155.sol new file mode 100644 index 0000000000..b715daa6dc --- /dev/null +++ b/eth/nft/contracts/MultiERC1155.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts@5.4.0/token/ERC1155/ERC1155.sol"; +import "@openzeppelin/contracts@5.4.0/access/Ownable.sol"; +import "@openzeppelin/contracts@5.4.0/utils/Base64.sol"; +import "@openzeppelin/contracts@5.4.0/utils/Strings.sol"; + +/// @title MultiERC1155 +/// @notice ERC1155 contract with sequential variants for immutable metadata. +/// @dev Non-upgradeable. Admin and minter addresses are settable by owner. Global sequential token IDs. +contract MultiERC1155 is ERC1155, Ownable { + address public admin; // can manage token IDs + address public minter; // can mint tokens for existing IDs + bool public mintingEnabled; + bool public contractLocked; // no more variants can be added, minting cannot be enabled, cannot be unlocked + + // for name and description avoid or escape double quotes, so it can be used in JSON + struct TokenInfo { + string tokenUri; + uint totalSupply; // 0 for unlimited + bool enabled; + } + + struct TokenState { + TokenInfo tokenInfo; + uint currentSupply; + bool exists; + bool locked; + } + + uint private _nextTokenId; // Global sequential token ID (starts at 1) + mapping(uint => TokenState) public tokens; + uint[] public tokenIds; + + function getTokenIds() view external returns(uint[] memory) { + return tokenIds; + } + + event AdminUpdated(address indexed newAdmin); + event MinterUpdated(address indexed newMinter); + event MintingEnabled(bool newEnabled); + event ContractLocked(); + event TokenAdded(uint indexed tokenId); + event TokenRemoved(uint indexed tokenId); + event TokenUpdated(uint indexed tokenId, bool newEnabled, uint newTotalSupply); + event TokenLocked(uint indexed tokenId); + + constructor() ERC1155("") Ownable(msg.sender) { + admin = msg.sender; + minter = msg.sender; + _nextTokenId = 1; + mintingEnabled = true; + } + + /// @notice Updates the minter address. + /// @param newAdmin The new minter. + function setAdmin(address newAdmin) external onlyOwner { + admin = newAdmin; + emit AdminUpdated(newAdmin); + } + + /// @notice Updates the minter address. + /// @param newMinter The new minter. + function setMinter(address newMinter) external onlyOwner { + minter = newMinter; + emit MinterUpdated(newMinter); + } + + /// @notice Enables/disables minting. + /// @param enabled True/false to enable/disable minting. + function toggleMinting(bool enabled) external onlyOwner { + if (mintingEnabled != enabled) { + mintingEnabled = enabled; + emit MintingEnabled(enabled); + } + } + + /// @notice Permanently locks any token changes and minting, irreversible. + function lockContract() external onlyOwner { + contractLocked = true; + mintingEnabled = false; + emit ContractLocked(); + } + + /// @notice Adds a new variant and optionally sets it as current. + /// @param info New token info. + function addToken(TokenInfo memory info) external { + require(msg.sender == admin || msg.sender == owner(), "Only admin and owner can add tokens"); + _addToken(info); + } + + function _addToken(TokenInfo memory info) internal { + require(bytes(info.tokenUri).length > 0, "Token tokenUri required"); + + uint id = _nextTokenId++; + require(!tokens[id].exists, "Contract error: token ID already exists"); + + tokens[id] = TokenState({ + tokenInfo: info, + currentSupply: 0, + exists: true, + locked: false + }); + tokenIds.push(id); + + emit TokenAdded(id); + } + + /// @notice Removes the last variant if unused (currentSupply == 0). + function removeToken(uint id) external { + require(msg.sender == admin || msg.sender == owner(), "Only admin and owner can remove tokens"); + TokenState storage token = tokens[id]; + require(token.exists, "Token ID does not exist"); + require(token.currentSupply == 0, "Tokens already minted for this ID"); + require(tokenIds.length > 1, "Cannot remove the last token ID"); + + delete tokens[id]; + for (uint i = 0; i < tokenIds.length; i++) { + if (tokenIds[i] == id) { + tokenIds[i] = tokenIds[tokenIds.length - 1]; + tokenIds.pop(); + break; + } + } + emit TokenRemoved(id); + } + + + /// @notice Enables/disables minting a specific token. + /// @param id Token ID. + /// @param newEnabled True/false to enable/disable minting. + /// @param newTotalSupply 0 for unlimited + function updateToken(uint id, bool newEnabled, uint newTotalSupply) external { + require(msg.sender == admin || msg.sender == owner(), "Only admin and owner can remove tokens"); + TokenState storage token = tokens[id]; + require(token.exists, "Token ID does not exist"); + require(!token.locked, "Token ID is locked"); + require(newTotalSupply == 0 || token.currentSupply <= newTotalSupply, "New total supply must be greater than existing token count for ID"); + + if (token.tokenInfo.enabled != newEnabled || token.tokenInfo.totalSupply != newTotalSupply) { + token.tokenInfo.enabled = newEnabled; + token.tokenInfo.totalSupply = newTotalSupply; + emit TokenUpdated(id, newEnabled, newTotalSupply); + } + } + + /// @notice permanently lock a specific token from any further changes. + /// @param id Token ID. + function lockToken(uint id) external onlyOwner { + TokenState storage token = tokens[id]; + require(token.exists, "Token ID does not exist"); + require(!token.locked, "Token ID is already locked"); + require(token.currentSupply != 0, "No tokens minted for this ID, use removeToken instead"); + token.locked = true; + token.tokenInfo.enabled = false; + emit TokenLocked(id); + } + + /// @notice Mints a token using the default variant. + /// @param to Recipient. + /// @param id Token ID. + /// @param value Amount to mint. + /// @param data Optional data. + function mint(address to, uint256 id, uint256 value, bytes calldata data) external { + require(!contractLocked, "Contract is permanently locked"); + require(mintingEnabled, "Minting is disabled"); + require(msg.sender == minter || msg.sender == admin || msg.sender == owner(), "Only minter, admin or owner can mint"); + TokenState storage token = tokens[id]; + require(token.exists, "Token ID does not exist"); + require(!token.locked, "Token ID is locked"); + require(token.tokenInfo.enabled, "Token ID is disabled"); + require(token.tokenInfo.totalSupply == 0 || token.tokenInfo.totalSupply >= token.currentSupply + value, "Token supply exceeded for this ID"); + require(value > 0, "Amount must be > 0"); + + _mint(to, id, value, data); + } + + /// @dev Hook to update supplies on transfer/burn. + function _update(address from, address to, uint[] memory ids, uint[] memory values) internal virtual override { + super._update(from, to, ids, values); + + for (uint i = 0; i < ids.length; i++) { + if (from == address(0)) { // mint + tokens[ids[i]].currentSupply += values[i]; + } + if (to == address(0)) { // burn + tokens[ids[i]].currentSupply -= values[i]; + } + } + } + + /// @notice Returns embedded JSON metadata URI. + /// @param id Token ID. + function uri(uint id) public view virtual override returns (string memory) { + TokenState storage token = tokens[id]; + require(token.exists, "Invalid token ID"); + return token.tokenInfo.tokenUri; + } +} diff --git a/eth/nft/contracts/NFTMinter.sol b/eth/nft/contracts/NFTMinter.sol new file mode 100644 index 0000000000..cce14909b9 --- /dev/null +++ b/eth/nft/contracts/NFTMinter.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts@5.4.0/access/Ownable.sol"; +import "@openzeppelin/contracts@5.4.0/utils/Pausable.sol"; +import "./NFTNumbered.sol"; + +contract NFTMinter is Ownable, Pausable { + NFTNumbered public nft; + uint public mintStartTime; + uint public mintEndTime; + uint public mintCount; + + /// @param _mintEndTime Unix timestamp when minting ends, 0 to mint without time limit. + constructor(address _nftAddress, uint _mintStartTime, uint _mintEndTime, bool _paused) Ownable(msg.sender) { + _setNFT(_nftAddress); + require(_mintEndTime == 0 || _mintEndTime > _mintStartTime, "Mint end time is before start time"); + require(_mintEndTime == 0 || _mintEndTime > block.timestamp, "Mint end time is in the past"); + mintStartTime = _mintStartTime; + mintEndTime = _mintEndTime; + if (_paused) _pause(); + } + + event NFTUpdated(address indexed newNFT); + event Minted(address indexed to, uint count); + event MintStartUpdated(uint newTime); + event MintEndUpdated(uint newTime); + event MintCountReset(uint oldCount); + + /// @notice Allows anyone to mint NFT for free (gas only). Any mint restrictions other than not paused or mint time must be in NFT contract + function mint() external whenNotPaused { + require(mintEndTime == 0 || mintEndTime > block.timestamp, "Minting ended"); + require(mintStartTime <= block.timestamp, "Minting not started"); + nft.mint(msg.sender); + mintCount++; + emit Minted(msg.sender, mintCount); + } + + /// @notice Update the target NFT contract. + /// @param newNFT The new NFT contract address. + function setNFT(address newNFT) external onlyOwner { + _setNFT(newNFT); + emit NFTUpdated(newNFT); + } + + function _setNFT(address _nft) internal { + require(_nft != address(0), "NFT contract address is 0"); + uint codeSize; + assembly { codeSize := extcodesize(_nft) } + require(codeSize > 0, "Not a contract address"); + nft = NFTNumbered(_nft); + } + + /// @notice Reset mint counter. + function resetMintCount() external onlyOwner { + uint old = mintCount; + mintCount = 0; + emit MintCountReset(old); + } + + /// @notice Update the mint start timestamp. + /// @param newTime The new Unix timestamp when minting ends. + function setMintStartTime(uint256 newTime) external onlyOwner { + require(mintEndTime == 0 || newTime < mintEndTime, "Mint end time is before start time"); + mintStartTime = newTime; + emit MintStartUpdated(newTime); + } + + /// @notice Update the mint end timestamp. + /// @param newTime The new Unix timestamp when minting ends, 0 to mint without time limit. + function setMintEndTime(uint256 newTime) external onlyOwner { + require(newTime == 0 || newTime > mintStartTime, "Mint end time is before start time"); + require(newTime == 0 || newTime > block.timestamp, "Mint end time is in the past"); + mintEndTime = newTime; + emit MintEndUpdated(newTime); + } + + /// @notice Pause minting. + function pause() external onlyOwner { + _pause(); + } + + /// @notice Unpause minting. + function unpause() external onlyOwner { + _unpause(); + } + + /// @notice Withdraw any accidental ETH. + function withdraw() external onlyOwner { + payable(owner()).transfer(address(this).balance); + } +} diff --git a/eth/nft/contracts/NFTMinter_flattened.sol b/eth/nft/contracts/NFTMinter_flattened.sol new file mode 100644 index 0000000000..01b65c761d --- /dev/null +++ b/eth/nft/contracts/NFTMinter_flattened.sol @@ -0,0 +1,4100 @@ +// SPDX-License-Identifier: MIT + +// File: @openzeppelin/contracts@5.4.0/utils/Context.sol + + +// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + +// File: @openzeppelin/contracts@5.4.0/access/Ownable.sol + + +// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * The initial owner is set to the address provided by the deployer. This can + * later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + /** + * @dev The caller account is not authorized to perform an operation. + */ + error OwnableUnauthorizedAccount(address account); + + /** + * @dev The owner is not a valid owner account. (eg. `address(0)`) + */ + error OwnableInvalidOwner(address owner); + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the address provided by the deployer as the initial owner. + */ + constructor(address initialOwner) { + if (initialOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(initialOwner); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + if (owner() != _msgSender()) { + revert OwnableUnauthorizedAccount(_msgSender()); + } + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby disabling any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + if (newOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + address oldOwner = _owner; + _owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/Pausable.sol + + +// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Contract module which allows children to implement an emergency stop + * mechanism that can be triggered by an authorized account. + * + * This module is used through inheritance. It will make available the + * modifiers `whenNotPaused` and `whenPaused`, which can be applied to + * the functions of your contract. Note that they will not be pausable by + * simply including this module, only once the modifiers are put in place. + */ +abstract contract Pausable is Context { + bool private _paused; + + /** + * @dev Emitted when the pause is triggered by `account`. + */ + event Paused(address account); + + /** + * @dev Emitted when the pause is lifted by `account`. + */ + event Unpaused(address account); + + /** + * @dev The operation failed because the contract is paused. + */ + error EnforcedPause(); + + /** + * @dev The operation failed because the contract is not paused. + */ + error ExpectedPause(); + + /** + * @dev Modifier to make a function callable only when the contract is not paused. + * + * Requirements: + * + * - The contract must not be paused. + */ + modifier whenNotPaused() { + _requireNotPaused(); + _; + } + + /** + * @dev Modifier to make a function callable only when the contract is paused. + * + * Requirements: + * + * - The contract must be paused. + */ + modifier whenPaused() { + _requirePaused(); + _; + } + + /** + * @dev Returns true if the contract is paused, and false otherwise. + */ + function paused() public view virtual returns (bool) { + return _paused; + } + + /** + * @dev Throws if the contract is paused. + */ + function _requireNotPaused() internal view virtual { + if (paused()) { + revert EnforcedPause(); + } + } + + /** + * @dev Throws if the contract is not paused. + */ + function _requirePaused() internal view virtual { + if (!paused()) { + revert ExpectedPause(); + } + } + + /** + * @dev Triggers stopped state. + * + * Requirements: + * + * - The contract must not be paused. + */ + function _pause() internal virtual whenNotPaused { + _paused = true; + emit Paused(_msgSender()); + } + + /** + * @dev Returns to normal state. + * + * Requirements: + * + * - The contract must be paused. + */ + function _unpause() internal virtual whenPaused { + _paused = false; + emit Unpaused(_msgSender()); + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/introspection/IERC165.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol) + +pragma solidity >=0.4.16; + +/** + * @dev Interface of the ERC-165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[ERC]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/IERC721.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol) + +pragma solidity >=0.6.2; + + +/** + * @dev Required interface of an ERC-721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon + * a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC-721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or + * {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon + * a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom(address from, address to, uint256 tokenId) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721 + * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must + * understand this adds an external call which potentially creates a reentrancy vulnerability. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 tokenId) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the address zero. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool approved) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/extensions/IERC721Metadata.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/IERC721Metadata.sol) + +pragma solidity >=0.6.2; + + +/** + * @title ERC-721 Non-Fungible Token Standard, optional metadata extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Metadata is IERC721 { + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/IERC721Receiver.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721Receiver.sol) + +pragma solidity >=0.5.0; + +/** + * @title ERC-721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC-721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} + * by `operator` from `from`, this function is called. + * + * It must return its Solidity selector to confirm the token transfer. + * If any other value is returned or the interface is not implemented by the recipient, the transfer will be + * reverted. + * + * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} + +// File: @openzeppelin/contracts@5.4.0/interfaces/draft-IERC6093.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol) +pragma solidity >=0.8.4; + +/** + * @dev Standard ERC-20 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens. + */ +interface IERC20Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC20InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC20InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` is allowed to operate with. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC20InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `spender` to be approved. Used in approvals. + * @param spender Address that may be allowed to operate on tokens without being their owner. + */ + error ERC20InvalidSpender(address spender); +} + +/** + * @dev Standard ERC-721 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens. + */ +interface IERC721Errors { + /** + * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20. + * Used in balance queries. + * @param owner Address of the current owner of a token. + */ + error ERC721InvalidOwner(address owner); + + /** + * @dev Indicates a `tokenId` whose `owner` is the zero address. + * @param tokenId Identifier number of a token. + */ + error ERC721NonexistentToken(uint256 tokenId); + + /** + * @dev Indicates an error related to the ownership over a particular token. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param tokenId Identifier number of a token. + * @param owner Address of the current owner of a token. + */ + error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC721InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC721InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param tokenId Identifier number of a token. + */ + error ERC721InsufficientApproval(address operator, uint256 tokenId); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC721InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC721InvalidOperator(address operator); +} + +/** + * @dev Standard ERC-1155 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens. + */ +interface IERC1155Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + * @param tokenId Identifier number of a token. + */ + error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC1155InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC1155InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param owner Address of the current owner of a token. + */ + error ERC1155MissingApprovalForAll(address operator, address owner); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC1155InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC1155InvalidOperator(address operator); + + /** + * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. + * Used in batch transfers. + * @param idsLength Length of the array of token identifiers + * @param valuesLength Length of the array of token amounts + */ + error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/utils/ERC721Utils.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/utils/ERC721Utils.sol) + +pragma solidity ^0.8.20; + + + +/** + * @dev Library that provide common ERC-721 utility functions. + * + * See https://eips.ethereum.org/EIPS/eip-721[ERC-721]. + * + * _Available since v5.1._ + */ +library ERC721Utils { + /** + * @dev Performs an acceptance check for the provided `operator` by calling {IERC721Receiver-onERC721Received} + * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`). + * + * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA). + * Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept + * the transfer. + */ + function checkOnERC721Received( + address operator, + address from, + address to, + uint256 tokenId, + bytes memory data + ) internal { + if (to.code.length > 0) { + try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) { + if (retval != IERC721Receiver.onERC721Received.selector) { + // Token rejected + revert IERC721Errors.ERC721InvalidReceiver(to); + } + } catch (bytes memory reason) { + if (reason.length == 0) { + // non-IERC721Receiver implementer + revert IERC721Errors.ERC721InvalidReceiver(to); + } else { + assembly ("memory-safe") { + revert(add(reason, 0x20), mload(reason)) + } + } + } + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/Panic.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Helper library for emitting standardized panic codes. + * + * ```solidity + * contract Example { + * using Panic for uint256; + * + * // Use any of the declared internal constants + * function foo() { Panic.GENERIC.panic(); } + * + * // Alternatively + * function foo() { Panic.panic(Panic.GENERIC); } + * } + * ``` + * + * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil]. + * + * _Available since v5.1._ + */ +// slither-disable-next-line unused-state +library Panic { + /// @dev generic / unspecified error + uint256 internal constant GENERIC = 0x00; + /// @dev used by the assert() builtin + uint256 internal constant ASSERT = 0x01; + /// @dev arithmetic underflow or overflow + uint256 internal constant UNDER_OVERFLOW = 0x11; + /// @dev division or modulo by zero + uint256 internal constant DIVISION_BY_ZERO = 0x12; + /// @dev enum conversion error + uint256 internal constant ENUM_CONVERSION_ERROR = 0x21; + /// @dev invalid encoding in storage + uint256 internal constant STORAGE_ENCODING_ERROR = 0x22; + /// @dev empty array pop + uint256 internal constant EMPTY_ARRAY_POP = 0x31; + /// @dev array out of bounds access + uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32; + /// @dev resource error (too large allocation or too large array) + uint256 internal constant RESOURCE_ERROR = 0x41; + /// @dev calling invalid internal function + uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51; + + /// @dev Reverts with a panic code. Recommended to use with + /// the internal constants with predefined codes. + function panic(uint256 code) internal pure { + assembly ("memory-safe") { + mstore(0x00, 0x4e487b71) + mstore(0x20, code) + revert(0x1c, 0x24) + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/math/SafeCast.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol) +// This file was procedurally generated from scripts/generate/templates/SafeCast.js. + +pragma solidity ^0.8.20; + +/** + * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeCast { + /** + * @dev Value doesn't fit in an uint of `bits` size. + */ + error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); + + /** + * @dev An int value doesn't fit in an uint of `bits` size. + */ + error SafeCastOverflowedIntToUint(int256 value); + + /** + * @dev Value doesn't fit in an int of `bits` size. + */ + error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); + + /** + * @dev An uint value doesn't fit in an int of `bits` size. + */ + error SafeCastOverflowedUintToInt(uint256 value); + + /** + * @dev Returns the downcasted uint248 from uint256, reverting on + * overflow (when the input is greater than largest uint248). + * + * Counterpart to Solidity's `uint248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + */ + function toUint248(uint256 value) internal pure returns (uint248) { + if (value > type(uint248).max) { + revert SafeCastOverflowedUintDowncast(248, value); + } + return uint248(value); + } + + /** + * @dev Returns the downcasted uint240 from uint256, reverting on + * overflow (when the input is greater than largest uint240). + * + * Counterpart to Solidity's `uint240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + */ + function toUint240(uint256 value) internal pure returns (uint240) { + if (value > type(uint240).max) { + revert SafeCastOverflowedUintDowncast(240, value); + } + return uint240(value); + } + + /** + * @dev Returns the downcasted uint232 from uint256, reverting on + * overflow (when the input is greater than largest uint232). + * + * Counterpart to Solidity's `uint232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + */ + function toUint232(uint256 value) internal pure returns (uint232) { + if (value > type(uint232).max) { + revert SafeCastOverflowedUintDowncast(232, value); + } + return uint232(value); + } + + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toUint224(uint256 value) internal pure returns (uint224) { + if (value > type(uint224).max) { + revert SafeCastOverflowedUintDowncast(224, value); + } + return uint224(value); + } + + /** + * @dev Returns the downcasted uint216 from uint256, reverting on + * overflow (when the input is greater than largest uint216). + * + * Counterpart to Solidity's `uint216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + */ + function toUint216(uint256 value) internal pure returns (uint216) { + if (value > type(uint216).max) { + revert SafeCastOverflowedUintDowncast(216, value); + } + return uint216(value); + } + + /** + * @dev Returns the downcasted uint208 from uint256, reverting on + * overflow (when the input is greater than largest uint208). + * + * Counterpart to Solidity's `uint208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + */ + function toUint208(uint256 value) internal pure returns (uint208) { + if (value > type(uint208).max) { + revert SafeCastOverflowedUintDowncast(208, value); + } + return uint208(value); + } + + /** + * @dev Returns the downcasted uint200 from uint256, reverting on + * overflow (when the input is greater than largest uint200). + * + * Counterpart to Solidity's `uint200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + */ + function toUint200(uint256 value) internal pure returns (uint200) { + if (value > type(uint200).max) { + revert SafeCastOverflowedUintDowncast(200, value); + } + return uint200(value); + } + + /** + * @dev Returns the downcasted uint192 from uint256, reverting on + * overflow (when the input is greater than largest uint192). + * + * Counterpart to Solidity's `uint192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + */ + function toUint192(uint256 value) internal pure returns (uint192) { + if (value > type(uint192).max) { + revert SafeCastOverflowedUintDowncast(192, value); + } + return uint192(value); + } + + /** + * @dev Returns the downcasted uint184 from uint256, reverting on + * overflow (when the input is greater than largest uint184). + * + * Counterpart to Solidity's `uint184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + */ + function toUint184(uint256 value) internal pure returns (uint184) { + if (value > type(uint184).max) { + revert SafeCastOverflowedUintDowncast(184, value); + } + return uint184(value); + } + + /** + * @dev Returns the downcasted uint176 from uint256, reverting on + * overflow (when the input is greater than largest uint176). + * + * Counterpart to Solidity's `uint176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + */ + function toUint176(uint256 value) internal pure returns (uint176) { + if (value > type(uint176).max) { + revert SafeCastOverflowedUintDowncast(176, value); + } + return uint176(value); + } + + /** + * @dev Returns the downcasted uint168 from uint256, reverting on + * overflow (when the input is greater than largest uint168). + * + * Counterpart to Solidity's `uint168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + */ + function toUint168(uint256 value) internal pure returns (uint168) { + if (value > type(uint168).max) { + revert SafeCastOverflowedUintDowncast(168, value); + } + return uint168(value); + } + + /** + * @dev Returns the downcasted uint160 from uint256, reverting on + * overflow (when the input is greater than largest uint160). + * + * Counterpart to Solidity's `uint160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + */ + function toUint160(uint256 value) internal pure returns (uint160) { + if (value > type(uint160).max) { + revert SafeCastOverflowedUintDowncast(160, value); + } + return uint160(value); + } + + /** + * @dev Returns the downcasted uint152 from uint256, reverting on + * overflow (when the input is greater than largest uint152). + * + * Counterpart to Solidity's `uint152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + */ + function toUint152(uint256 value) internal pure returns (uint152) { + if (value > type(uint152).max) { + revert SafeCastOverflowedUintDowncast(152, value); + } + return uint152(value); + } + + /** + * @dev Returns the downcasted uint144 from uint256, reverting on + * overflow (when the input is greater than largest uint144). + * + * Counterpart to Solidity's `uint144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + */ + function toUint144(uint256 value) internal pure returns (uint144) { + if (value > type(uint144).max) { + revert SafeCastOverflowedUintDowncast(144, value); + } + return uint144(value); + } + + /** + * @dev Returns the downcasted uint136 from uint256, reverting on + * overflow (when the input is greater than largest uint136). + * + * Counterpart to Solidity's `uint136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + */ + function toUint136(uint256 value) internal pure returns (uint136) { + if (value > type(uint136).max) { + revert SafeCastOverflowedUintDowncast(136, value); + } + return uint136(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toUint128(uint256 value) internal pure returns (uint128) { + if (value > type(uint128).max) { + revert SafeCastOverflowedUintDowncast(128, value); + } + return uint128(value); + } + + /** + * @dev Returns the downcasted uint120 from uint256, reverting on + * overflow (when the input is greater than largest uint120). + * + * Counterpart to Solidity's `uint120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + */ + function toUint120(uint256 value) internal pure returns (uint120) { + if (value > type(uint120).max) { + revert SafeCastOverflowedUintDowncast(120, value); + } + return uint120(value); + } + + /** + * @dev Returns the downcasted uint112 from uint256, reverting on + * overflow (when the input is greater than largest uint112). + * + * Counterpart to Solidity's `uint112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + */ + function toUint112(uint256 value) internal pure returns (uint112) { + if (value > type(uint112).max) { + revert SafeCastOverflowedUintDowncast(112, value); + } + return uint112(value); + } + + /** + * @dev Returns the downcasted uint104 from uint256, reverting on + * overflow (when the input is greater than largest uint104). + * + * Counterpart to Solidity's `uint104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + */ + function toUint104(uint256 value) internal pure returns (uint104) { + if (value > type(uint104).max) { + revert SafeCastOverflowedUintDowncast(104, value); + } + return uint104(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toUint96(uint256 value) internal pure returns (uint96) { + if (value > type(uint96).max) { + revert SafeCastOverflowedUintDowncast(96, value); + } + return uint96(value); + } + + /** + * @dev Returns the downcasted uint88 from uint256, reverting on + * overflow (when the input is greater than largest uint88). + * + * Counterpart to Solidity's `uint88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + */ + function toUint88(uint256 value) internal pure returns (uint88) { + if (value > type(uint88).max) { + revert SafeCastOverflowedUintDowncast(88, value); + } + return uint88(value); + } + + /** + * @dev Returns the downcasted uint80 from uint256, reverting on + * overflow (when the input is greater than largest uint80). + * + * Counterpart to Solidity's `uint80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + */ + function toUint80(uint256 value) internal pure returns (uint80) { + if (value > type(uint80).max) { + revert SafeCastOverflowedUintDowncast(80, value); + } + return uint80(value); + } + + /** + * @dev Returns the downcasted uint72 from uint256, reverting on + * overflow (when the input is greater than largest uint72). + * + * Counterpart to Solidity's `uint72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + */ + function toUint72(uint256 value) internal pure returns (uint72) { + if (value > type(uint72).max) { + revert SafeCastOverflowedUintDowncast(72, value); + } + return uint72(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toUint64(uint256 value) internal pure returns (uint64) { + if (value > type(uint64).max) { + revert SafeCastOverflowedUintDowncast(64, value); + } + return uint64(value); + } + + /** + * @dev Returns the downcasted uint56 from uint256, reverting on + * overflow (when the input is greater than largest uint56). + * + * Counterpart to Solidity's `uint56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + */ + function toUint56(uint256 value) internal pure returns (uint56) { + if (value > type(uint56).max) { + revert SafeCastOverflowedUintDowncast(56, value); + } + return uint56(value); + } + + /** + * @dev Returns the downcasted uint48 from uint256, reverting on + * overflow (when the input is greater than largest uint48). + * + * Counterpart to Solidity's `uint48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + */ + function toUint48(uint256 value) internal pure returns (uint48) { + if (value > type(uint48).max) { + revert SafeCastOverflowedUintDowncast(48, value); + } + return uint48(value); + } + + /** + * @dev Returns the downcasted uint40 from uint256, reverting on + * overflow (when the input is greater than largest uint40). + * + * Counterpart to Solidity's `uint40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + */ + function toUint40(uint256 value) internal pure returns (uint40) { + if (value > type(uint40).max) { + revert SafeCastOverflowedUintDowncast(40, value); + } + return uint40(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toUint32(uint256 value) internal pure returns (uint32) { + if (value > type(uint32).max) { + revert SafeCastOverflowedUintDowncast(32, value); + } + return uint32(value); + } + + /** + * @dev Returns the downcasted uint24 from uint256, reverting on + * overflow (when the input is greater than largest uint24). + * + * Counterpart to Solidity's `uint24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + */ + function toUint24(uint256 value) internal pure returns (uint24) { + if (value > type(uint24).max) { + revert SafeCastOverflowedUintDowncast(24, value); + } + return uint24(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toUint16(uint256 value) internal pure returns (uint16) { + if (value > type(uint16).max) { + revert SafeCastOverflowedUintDowncast(16, value); + } + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + */ + function toUint8(uint256 value) internal pure returns (uint8) { + if (value > type(uint8).max) { + revert SafeCastOverflowedUintDowncast(8, value); + } + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + */ + function toUint256(int256 value) internal pure returns (uint256) { + if (value < 0) { + revert SafeCastOverflowedIntToUint(value); + } + return uint256(value); + } + + /** + * @dev Returns the downcasted int248 from int256, reverting on + * overflow (when the input is less than smallest int248 or + * greater than largest int248). + * + * Counterpart to Solidity's `int248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + */ + function toInt248(int256 value) internal pure returns (int248 downcasted) { + downcasted = int248(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(248, value); + } + } + + /** + * @dev Returns the downcasted int240 from int256, reverting on + * overflow (when the input is less than smallest int240 or + * greater than largest int240). + * + * Counterpart to Solidity's `int240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + */ + function toInt240(int256 value) internal pure returns (int240 downcasted) { + downcasted = int240(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(240, value); + } + } + + /** + * @dev Returns the downcasted int232 from int256, reverting on + * overflow (when the input is less than smallest int232 or + * greater than largest int232). + * + * Counterpart to Solidity's `int232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + */ + function toInt232(int256 value) internal pure returns (int232 downcasted) { + downcasted = int232(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(232, value); + } + } + + /** + * @dev Returns the downcasted int224 from int256, reverting on + * overflow (when the input is less than smallest int224 or + * greater than largest int224). + * + * Counterpart to Solidity's `int224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toInt224(int256 value) internal pure returns (int224 downcasted) { + downcasted = int224(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(224, value); + } + } + + /** + * @dev Returns the downcasted int216 from int256, reverting on + * overflow (when the input is less than smallest int216 or + * greater than largest int216). + * + * Counterpart to Solidity's `int216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + */ + function toInt216(int256 value) internal pure returns (int216 downcasted) { + downcasted = int216(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(216, value); + } + } + + /** + * @dev Returns the downcasted int208 from int256, reverting on + * overflow (when the input is less than smallest int208 or + * greater than largest int208). + * + * Counterpart to Solidity's `int208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + */ + function toInt208(int256 value) internal pure returns (int208 downcasted) { + downcasted = int208(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(208, value); + } + } + + /** + * @dev Returns the downcasted int200 from int256, reverting on + * overflow (when the input is less than smallest int200 or + * greater than largest int200). + * + * Counterpart to Solidity's `int200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + */ + function toInt200(int256 value) internal pure returns (int200 downcasted) { + downcasted = int200(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(200, value); + } + } + + /** + * @dev Returns the downcasted int192 from int256, reverting on + * overflow (when the input is less than smallest int192 or + * greater than largest int192). + * + * Counterpart to Solidity's `int192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + */ + function toInt192(int256 value) internal pure returns (int192 downcasted) { + downcasted = int192(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(192, value); + } + } + + /** + * @dev Returns the downcasted int184 from int256, reverting on + * overflow (when the input is less than smallest int184 or + * greater than largest int184). + * + * Counterpart to Solidity's `int184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + */ + function toInt184(int256 value) internal pure returns (int184 downcasted) { + downcasted = int184(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(184, value); + } + } + + /** + * @dev Returns the downcasted int176 from int256, reverting on + * overflow (when the input is less than smallest int176 or + * greater than largest int176). + * + * Counterpart to Solidity's `int176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + */ + function toInt176(int256 value) internal pure returns (int176 downcasted) { + downcasted = int176(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(176, value); + } + } + + /** + * @dev Returns the downcasted int168 from int256, reverting on + * overflow (when the input is less than smallest int168 or + * greater than largest int168). + * + * Counterpart to Solidity's `int168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + */ + function toInt168(int256 value) internal pure returns (int168 downcasted) { + downcasted = int168(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(168, value); + } + } + + /** + * @dev Returns the downcasted int160 from int256, reverting on + * overflow (when the input is less than smallest int160 or + * greater than largest int160). + * + * Counterpart to Solidity's `int160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + */ + function toInt160(int256 value) internal pure returns (int160 downcasted) { + downcasted = int160(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(160, value); + } + } + + /** + * @dev Returns the downcasted int152 from int256, reverting on + * overflow (when the input is less than smallest int152 or + * greater than largest int152). + * + * Counterpart to Solidity's `int152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + */ + function toInt152(int256 value) internal pure returns (int152 downcasted) { + downcasted = int152(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(152, value); + } + } + + /** + * @dev Returns the downcasted int144 from int256, reverting on + * overflow (when the input is less than smallest int144 or + * greater than largest int144). + * + * Counterpart to Solidity's `int144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + */ + function toInt144(int256 value) internal pure returns (int144 downcasted) { + downcasted = int144(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(144, value); + } + } + + /** + * @dev Returns the downcasted int136 from int256, reverting on + * overflow (when the input is less than smallest int136 or + * greater than largest int136). + * + * Counterpart to Solidity's `int136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + */ + function toInt136(int256 value) internal pure returns (int136 downcasted) { + downcasted = int136(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(136, value); + } + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toInt128(int256 value) internal pure returns (int128 downcasted) { + downcasted = int128(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(128, value); + } + } + + /** + * @dev Returns the downcasted int120 from int256, reverting on + * overflow (when the input is less than smallest int120 or + * greater than largest int120). + * + * Counterpart to Solidity's `int120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + */ + function toInt120(int256 value) internal pure returns (int120 downcasted) { + downcasted = int120(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(120, value); + } + } + + /** + * @dev Returns the downcasted int112 from int256, reverting on + * overflow (when the input is less than smallest int112 or + * greater than largest int112). + * + * Counterpart to Solidity's `int112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + */ + function toInt112(int256 value) internal pure returns (int112 downcasted) { + downcasted = int112(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(112, value); + } + } + + /** + * @dev Returns the downcasted int104 from int256, reverting on + * overflow (when the input is less than smallest int104 or + * greater than largest int104). + * + * Counterpart to Solidity's `int104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + */ + function toInt104(int256 value) internal pure returns (int104 downcasted) { + downcasted = int104(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(104, value); + } + } + + /** + * @dev Returns the downcasted int96 from int256, reverting on + * overflow (when the input is less than smallest int96 or + * greater than largest int96). + * + * Counterpart to Solidity's `int96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toInt96(int256 value) internal pure returns (int96 downcasted) { + downcasted = int96(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(96, value); + } + } + + /** + * @dev Returns the downcasted int88 from int256, reverting on + * overflow (when the input is less than smallest int88 or + * greater than largest int88). + * + * Counterpart to Solidity's `int88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + */ + function toInt88(int256 value) internal pure returns (int88 downcasted) { + downcasted = int88(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(88, value); + } + } + + /** + * @dev Returns the downcasted int80 from int256, reverting on + * overflow (when the input is less than smallest int80 or + * greater than largest int80). + * + * Counterpart to Solidity's `int80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + */ + function toInt80(int256 value) internal pure returns (int80 downcasted) { + downcasted = int80(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(80, value); + } + } + + /** + * @dev Returns the downcasted int72 from int256, reverting on + * overflow (when the input is less than smallest int72 or + * greater than largest int72). + * + * Counterpart to Solidity's `int72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + */ + function toInt72(int256 value) internal pure returns (int72 downcasted) { + downcasted = int72(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(72, value); + } + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toInt64(int256 value) internal pure returns (int64 downcasted) { + downcasted = int64(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(64, value); + } + } + + /** + * @dev Returns the downcasted int56 from int256, reverting on + * overflow (when the input is less than smallest int56 or + * greater than largest int56). + * + * Counterpart to Solidity's `int56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + */ + function toInt56(int256 value) internal pure returns (int56 downcasted) { + downcasted = int56(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(56, value); + } + } + + /** + * @dev Returns the downcasted int48 from int256, reverting on + * overflow (when the input is less than smallest int48 or + * greater than largest int48). + * + * Counterpart to Solidity's `int48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + */ + function toInt48(int256 value) internal pure returns (int48 downcasted) { + downcasted = int48(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(48, value); + } + } + + /** + * @dev Returns the downcasted int40 from int256, reverting on + * overflow (when the input is less than smallest int40 or + * greater than largest int40). + * + * Counterpart to Solidity's `int40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + */ + function toInt40(int256 value) internal pure returns (int40 downcasted) { + downcasted = int40(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(40, value); + } + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toInt32(int256 value) internal pure returns (int32 downcasted) { + downcasted = int32(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(32, value); + } + } + + /** + * @dev Returns the downcasted int24 from int256, reverting on + * overflow (when the input is less than smallest int24 or + * greater than largest int24). + * + * Counterpart to Solidity's `int24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + */ + function toInt24(int256 value) internal pure returns (int24 downcasted) { + downcasted = int24(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(24, value); + } + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toInt16(int256 value) internal pure returns (int16 downcasted) { + downcasted = int16(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(16, value); + } + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + */ + function toInt8(int256 value) internal pure returns (int8 downcasted) { + downcasted = int8(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(8, value); + } + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + if (value > uint256(type(int256).max)) { + revert SafeCastOverflowedUintToInt(value); + } + return int256(value); + } + + /** + * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump. + */ + function toUint(bool b) internal pure returns (uint256 u) { + assembly ("memory-safe") { + u := iszero(iszero(b)) + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/math/Math.sol + + +// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol) + +pragma solidity ^0.8.20; + + + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + enum Rounding { + Floor, // Toward negative infinity + Ceil, // Toward positive infinity + Trunc, // Toward zero + Expand // Away from zero + } + + /** + * @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; + success = c >= a; + result = c * SafeCast.toUint(success); + } + } + + /** + * @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 { + uint256 c = a - b; + success = c <= a; + result = c * SafeCast.toUint(success); + } + } + + /** + * @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 { + uint256 c = a * b; + 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); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a success flag (no division by zero). + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { + unchecked { + success = b > 0; + assembly ("memory-safe") { + // The `DIV` opcode returns zero when the denominator is 0. + result := div(a, b) + } + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero). + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { + unchecked { + 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. + * + * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone. + * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute + * one branch when needed, making this function more expensive. + */ + function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) { + unchecked { + // branchless ternary works because: + // b ^ (a ^ b) == a + // b ^ 0 == b + return b ^ ((a ^ b) * SafeCast.toUint(condition)); + } + } + + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return ternary(a > b, a, b); + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return ternary(a < b, a, b); + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds towards infinity instead + * of rounding towards zero. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + if (b == 0) { + // Guarantee the same behavior as in a regular Solidity division. + Panic.panic(Panic.DIVISION_BY_ZERO); + } + + // The following calculation ensures accurate ceiling division without overflow. + // Since a is non-zero, (a - 1) / b will not overflow. + // The largest possible result occurs when (a - 1) / b is type(uint256).max, + // but the largest value we can obtain is type(uint256).max - 1, which happens + // when a = type(uint256).max and b = 1. + unchecked { + return SafeCast.toUint(a > 0) * ((a - 1) / b + 1); + } + } + + /** + * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or + * denominator == 0. + * + * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by + * Uniswap Labs also under MIT license. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + (uint256 high, uint256 low) = mul512(x, y); + + // Handle non-overflow cases, 256 by 256 division. + 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 low / denominator; + } + + // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0. + if (denominator <= high) { + Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW)); + } + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [high low]. + uint256 remainder; + assembly ("memory-safe") { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number. + 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 ("memory-safe") { + // Divide denominator by twos. + denominator := div(denominator, 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 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 + // four bits. That is, denominator * inv ≡ 1 mod 2⁴. + uint256 inverse = (3 * denominator) ^ 2; + + // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also + // works in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2⁸ + inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶ + inverse *= 2 - denominator * inverse; // inverse mod 2³² + inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴ + inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸ + inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶ + + // 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 high + // is no longer required. + result = low * inverse; + return result; + } + } + + /** + * @dev Calculates x * y / denominator with full precision, following the selected rounding direction. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { + 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. + * + * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0. + * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible. + * + * If the input value is not inversible, 0 is returned. + * + * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the + * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}. + */ + function invMod(uint256 a, uint256 n) internal pure returns (uint256) { + unchecked { + if (n == 0) return 0; + + // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version) + // Used to compute integers x and y such that: ax + ny = gcd(a, n). + // When the gcd is 1, then the inverse of a modulo n exists and it's x. + // ax + ny = 1 + // ax = 1 + (-y)n + // ax ≡ 1 (mod n) # x is the inverse of a modulo n + + // If the remainder is 0 the gcd is n right away. + uint256 remainder = a % n; + uint256 gcd = n; + + // Therefore the initial coefficients are: + // ax + ny = gcd(a, n) = n + // 0a + 1n = n + int256 x = 0; + int256 y = 1; + + while (remainder != 0) { + uint256 quotient = gcd / remainder; + + (gcd, remainder) = ( + // The old remainder is the next gcd to try. + remainder, + // Compute the next remainder. + // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd + // where gcd is at most n (capped to type(uint256).max) + gcd - remainder * quotient + ); + + (x, y) = ( + // Increment the coefficient of a. + y, + // Decrement the coefficient of n. + // Can overflow, but the result is casted to uint256 so that the + // next value of y is "wrapped around" to a value between 0 and n - 1. + x - y * int256(quotient) + ); + } + + if (gcd != 1) return 0; // No inverse exists. + return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative. + } + } + + /** + * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`. + * + * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is + * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that + * `a**(p-2)` is the modular multiplicative inverse of a in Fp. + * + * NOTE: this function does NOT check that `p` is a prime greater than `2`. + */ + function invModPrime(uint256 a, uint256 p) internal view returns (uint256) { + unchecked { + return Math.modExp(a, p - 2, p); + } + } + + /** + * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m) + * + * Requirements: + * - modulus can't be zero + * - underlying staticcall to precompile must succeed + * + * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make + * sure the chain you're using it on supports the precompiled contract for modular exponentiation + * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, + * the underlying function will succeed given the lack of a revert, but the result may be incorrectly + * interpreted as 0. + */ + function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) { + (bool success, uint256 result) = tryModExp(b, e, m); + if (!success) { + Panic.panic(Panic.DIVISION_BY_ZERO); + } + return result; + } + + /** + * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m). + * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying + * to operate modulo 0 or if the underlying precompile reverted. + * + * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain + * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in + * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack + * of a revert, but the result may be incorrectly interpreted as 0. + */ + function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) { + if (m == 0) return (false, 0); + assembly ("memory-safe") { + let ptr := mload(0x40) + // | Offset | Content | Content (Hex) | + // |-----------|------------|--------------------------------------------------------------------| + // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 | + // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 | + // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 | + // | 0x60:0x7f | value of b | 0x<.............................................................b> | + // | 0x80:0x9f | value of e | 0x<.............................................................e> | + // | 0xa0:0xbf | value of m | 0x<.............................................................m> | + mstore(ptr, 0x20) + mstore(add(ptr, 0x20), 0x20) + mstore(add(ptr, 0x40), 0x20) + mstore(add(ptr, 0x60), b) + mstore(add(ptr, 0x80), e) + mstore(add(ptr, 0xa0), m) + + // Given the result < m, it's guaranteed to fit in 32 bytes, + // so we can use the memory scratch space located at offset 0. + success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20) + result := mload(0x00) + } + } + + /** + * @dev Variant of {modExp} that supports inputs of arbitrary length. + */ + function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) { + (bool success, bytes memory result) = tryModExp(b, e, m); + if (!success) { + Panic.panic(Panic.DIVISION_BY_ZERO); + } + return result; + } + + /** + * @dev Variant of {tryModExp} that supports inputs of arbitrary length. + */ + function tryModExp( + bytes memory b, + bytes memory e, + bytes memory m + ) internal view returns (bool success, bytes memory result) { + if (_zeroBytes(m)) return (false, new bytes(0)); + + uint256 mLen = m.length; + + // Encode call args in result and move the free memory pointer + result = abi.encodePacked(b.length, e.length, mLen, b, e, m); + + assembly ("memory-safe") { + let dataPtr := add(result, 0x20) + // Write result on top of args to avoid allocating extra memory. + success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen) + // Overwrite the length. + // result.length > returndatasize() is guaranteed because returndatasize() == m.length + mstore(result, mLen) + // Set the memory pointer after the returned data. + mstore(0x40, add(dataPtr, mLen)) + } + } + + /** + * @dev Returns whether the provided byte array is zero. + */ + function _zeroBytes(bytes memory byteArray) private pure returns (bool) { + for (uint256 i = 0; i < byteArray.length; ++i) { + if (byteArray[i] != 0) { + return false; + } + } + return true; + } + + /** + * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded + * towards zero. + * + * This method is based on Newton's method for computing square roots; the algorithm is restricted to only + * using integer operations. + */ + function sqrt(uint256 a) internal pure returns (uint256) { + unchecked { + // Take care of easy edge cases when a == 0 or a == 1 + if (a <= 1) { + return a; + } + + // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a + // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between + // the current value as `ε_n = | x_n - sqrt(a) |`. + // + // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root + // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is + // bigger than any uint256. + // + // By noticing that + // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)` + // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar + // to the msb function. + uint256 aa = a; + uint256 xn = 1; + + if (aa >= (1 << 128)) { + aa >>= 128; + xn <<= 64; + } + if (aa >= (1 << 64)) { + aa >>= 64; + xn <<= 32; + } + if (aa >= (1 << 32)) { + aa >>= 32; + xn <<= 16; + } + if (aa >= (1 << 16)) { + aa >>= 16; + xn <<= 8; + } + if (aa >= (1 << 8)) { + aa >>= 8; + xn <<= 4; + } + if (aa >= (1 << 4)) { + aa >>= 4; + xn <<= 2; + } + if (aa >= (1 << 2)) { + xn <<= 1; + } + + // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1). + // + // We can refine our estimation by noticing that the middle of that interval minimizes the error. + // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2). + // This is going to be our x_0 (and ε_0) + xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2) + + // From here, Newton's method give us: + // x_{n+1} = (x_n + a / x_n) / 2 + // + // One should note that: + // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a + // = ((x_n² + a) / (2 * x_n))² - a + // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a + // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²) + // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²) + // = (x_n² - a)² / (2 * x_n)² + // = ((x_n² - a) / (2 * x_n))² + // ≥ 0 + // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n + // + // This gives us the proof of quadratic convergence of the sequence: + // ε_{n+1} = | x_{n+1} - sqrt(a) | + // = | (x_n + a / x_n) / 2 - sqrt(a) | + // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) | + // = | (x_n - sqrt(a))² / (2 * x_n) | + // = | ε_n² / (2 * x_n) | + // = ε_n² / | (2 * x_n) | + // + // For the first iteration, we have a special case where x_0 is known: + // ε_1 = ε_0² / | (2 * x_0) | + // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2))) + // ≤ 2**(2*e-4) / (3 * 2**(e-1)) + // ≤ 2**(e-3) / 3 + // ≤ 2**(e-3-log2(3)) + // ≤ 2**(e-4.5) + // + // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n: + // ε_{n+1} = ε_n² / | (2 * x_n) | + // ≤ (2**(e-k))² / (2 * 2**(e-1)) + // ≤ 2**(2*e-2*k) / 2**e + // ≤ 2**(e-2*k) + xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above + xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5 + xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9 + xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18 + xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36 + xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72 + + // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision + // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either + // sqrt(a) or sqrt(a) + 1. + return xn - SafeCast.toUint(xn > a / xn); + } + } + + /** + * @dev Calculates sqrt(a), following the selected rounding direction. + */ + function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = sqrt(a); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a); + } + } + + /** + * @dev Return the log in base 2 of a positive value rounded towards zero. + * Returns 0 if given 0. + */ + 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; + + // 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)) + } + } + + /** + * @dev Return the log in base 2, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log2(value); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value); + } + } + + /** + * @dev Return the log in base 10 of a positive value rounded towards zero. + * Returns 0 if given 0. + */ + function log10(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >= 10 ** 64) { + value /= 10 ** 64; + result += 64; + } + if (value >= 10 ** 32) { + value /= 10 ** 32; + result += 32; + } + if (value >= 10 ** 16) { + value /= 10 ** 16; + result += 16; + } + if (value >= 10 ** 8) { + value /= 10 ** 8; + result += 8; + } + if (value >= 10 ** 4) { + value /= 10 ** 4; + result += 4; + } + if (value >= 10 ** 2) { + value /= 10 ** 2; + result += 2; + } + if (value >= 10 ** 1) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log10(value); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value); + } + } + + /** + * @dev Return the log in base 256 of a positive value rounded towards zero. + * Returns 0 if given 0. + * + * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. + */ + 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); + } + + /** + * @dev Return the log in base 256, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log256(value); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value); + } + } + + /** + * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. + */ + function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { + return uint8(rounding) % 2 == 1; + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/math/SignedMath.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Standard signed math utilities missing in the Solidity language. + */ +library SignedMath { + /** + * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant. + * + * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone. + * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute + * one branch when needed, making this function more expensive. + */ + function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) { + unchecked { + // branchless ternary works because: + // b ^ (a ^ b) == a + // b ^ 0 == b + return b ^ ((a ^ b) * int256(SafeCast.toUint(condition))); + } + } + + /** + * @dev Returns the largest of two signed numbers. + */ + function max(int256 a, int256 b) internal pure returns (int256) { + return ternary(a > b, a, b); + } + + /** + * @dev Returns the smallest of two signed numbers. + */ + function min(int256 a, int256 b) internal pure returns (int256) { + return ternary(a < b, a, b); + } + + /** + * @dev Returns the average of two signed numbers without overflow. + * The result is rounded towards zero. + */ + function average(int256 a, int256 b) internal pure returns (int256) { + // Formula from the book "Hacker's Delight" + int256 x = (a & b) + ((a ^ b) >> 1); + return x + (int256(uint256(x) >> 255) & (a ^ b)); + } + + /** + * @dev Returns the absolute unsigned value of a signed value. + */ + function abs(int256 n) internal pure returns (uint256) { + unchecked { + // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson. + // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift, + // taking advantage of the most significant (or "sign" bit) in two's complement representation. + // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result, + // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative). + int256 mask = n >> 255; + + // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it. + return uint256((n + mask) ^ mask); + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/Strings.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (utils/Strings.sol) + +pragma solidity ^0.8.20; + + + + +/** + * @dev String operations. + */ +library Strings { + using SafeCast for *; + + 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`. + */ + error StringsInsufficientHexLength(uint256 value, uint256 length); + + /** + * @dev The string being parsed contains characters that are not in scope of the given base. + */ + error StringsInvalidChar(); + + /** + * @dev The string being parsed is not a properly formatted address. + */ + error StringsInvalidAddressFormat(); + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + unchecked { + uint256 length = Math.log10(value) + 1; + string memory buffer = new string(length); + uint256 ptr; + assembly ("memory-safe") { + ptr := add(add(buffer, 0x20), length) + } + while (true) { + ptr--; + assembly ("memory-safe") { + mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) + } + value /= 10; + if (value == 0) break; + } + return buffer; + } + } + + /** + * @dev Converts a `int256` to its ASCII `string` decimal representation. + */ + function toStringSigned(int256 value) internal pure returns (string memory) { + return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + unchecked { + return toHexString(value, Math.log256(value) + 1); + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + uint256 localValue = value; + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = HEX_DIGITS[localValue & 0xf]; + localValue >>= 4; + } + if (localValue != 0) { + revert StringsInsufficientHexLength(value, length); + } + return string(buffer); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal + * representation. + */ + function toHexString(address addr) internal pure returns (string memory) { + return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal + * representation, according to EIP-55. + */ + function toChecksumHexString(address addr) internal pure returns (string memory) { + bytes memory buffer = bytes(toHexString(addr)); + + // hash the hex part of buffer (skip length + 2 bytes, length 40) + uint256 hashValue; + assembly ("memory-safe") { + hashValue := shr(96, keccak256(add(buffer, 0x22), 40)) + } + + for (uint256 i = 41; i > 1; --i) { + // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f) + if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) { + // case shift by xoring with 0x20 + buffer[i] ^= 0x20; + } + hashValue >>= 4; + } + return string(buffer); + } + + /** + * @dev Returns true if the two strings are equal. + */ + function equal(string memory a, string memory b) internal pure returns (bool) { + return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); + } + + /** + * @dev Parse a decimal string and returns the value as a `uint256`. + * + * Requirements: + * - The string must be formatted as `[0-9]*` + * - The result must fit into an `uint256` type + */ + function parseUint(string memory input) internal pure returns (uint256) { + return parseUint(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `[0-9]*` + * - The result must fit into an `uint256` type + */ + function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) { + (bool success, uint256 value) = tryParseUint(input, begin, end); + if (!success) revert StringsInvalidChar(); + return value; + } + + /** + * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) { + return _tryParseUintUncheckedBounds(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid + * character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseUint( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, uint256 value) { + if (end > bytes(input).length || begin > end) return (false, 0); + return _tryParseUintUncheckedBounds(input, begin, end); + } + + /** + * @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( + string memory input, + uint256 begin, + uint256 end + ) private pure returns (bool success, uint256 value) { + bytes memory buffer = bytes(input); + + uint256 result = 0; + for (uint256 i = begin; i < end; ++i) { + uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i))); + if (chr > 9) return (false, 0); + result *= 10; + result += chr; + } + return (true, result); + } + + /** + * @dev Parse a decimal string and returns the value as a `int256`. + * + * Requirements: + * - The string must be formatted as `[-+]?[0-9]*` + * - The result must fit in an `int256` type. + */ + function parseInt(string memory input) internal pure returns (int256) { + return parseInt(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `[-+]?[0-9]*` + * - The result must fit in an `int256` type. + */ + function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) { + (bool success, int256 value) = tryParseInt(input, begin, end); + if (!success) revert StringsInvalidChar(); + return value; + } + + /** + * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if + * the result does not fit in a `int256`. + * + * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`. + */ + function tryParseInt(string memory input) internal pure returns (bool success, int256 value) { + return _tryParseIntUncheckedBounds(input, 0, bytes(input).length); + } + + uint256 private constant ABS_MIN_INT256 = 2 ** 255; + + /** + * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid + * character or if the result does not fit in a `int256`. + * + * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`. + */ + function tryParseInt( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, int256 value) { + if (end > bytes(input).length || begin > end) return (false, 0); + return _tryParseIntUncheckedBounds(input, begin, end); + } + + /** + * @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( + string memory input, + uint256 begin, + uint256 end + ) private pure returns (bool success, int256 value) { + bytes memory buffer = bytes(input); + + // Check presence of a negative sign. + bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty + bool positiveSign = sign == bytes1("+"); + bool negativeSign = sign == bytes1("-"); + uint256 offset = (positiveSign || negativeSign).toUint(); + + (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end); + + if (absSuccess && absValue < ABS_MIN_INT256) { + return (true, negativeSign ? -int256(absValue) : int256(absValue)); + } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) { + return (true, type(int256).min); + } else return (false, 0); + } + + /** + * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`. + * + * Requirements: + * - The string must be formatted as `(0x)?[0-9a-fA-F]*` + * - The result must fit in an `uint256` type. + */ + function parseHexUint(string memory input) internal pure returns (uint256) { + return parseHexUint(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `(0x)?[0-9a-fA-F]*` + * - The result must fit in an `uint256` type. + */ + function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) { + (bool success, uint256 value) = tryParseHexUint(input, begin, end); + if (!success) revert StringsInvalidChar(); + return value; + } + + /** + * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) { + return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an + * invalid character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseHexUint( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, uint256 value) { + if (end > bytes(input).length || begin > end) return (false, 0); + return _tryParseHexUintUncheckedBounds(input, begin, end); + } + + /** + * @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( + string memory input, + uint256 begin, + uint256 end + ) private pure returns (bool success, uint256 value) { + bytes memory buffer = bytes(input); + + // skip 0x prefix if present + bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty + uint256 offset = hasPrefix.toUint() * 2; + + uint256 result = 0; + for (uint256 i = begin + offset; i < end; ++i) { + uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i))); + if (chr > 15) return (false, 0); + result *= 16; + unchecked { + // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check). + // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked. + result += chr; + } + } + return (true, result); + } + + /** + * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`. + * + * Requirements: + * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}` + */ + function parseAddress(string memory input) internal pure returns (address) { + return parseAddress(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}` + */ + function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) { + (bool success, address value) = tryParseAddress(input, begin, end); + if (!success) revert StringsInvalidAddressFormat(); + return value; + } + + /** + * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly + * 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); + } + + /** + * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly + * formatted address. See {parseAddress-string-uint256-uint256} requirements. + */ + function tryParseAddress( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, address value) { + if (end > bytes(input).length || begin > end) return (false, address(0)); + + bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty + uint256 expectedLength = 40 + hasPrefix.toUint() * 2; + + // check that input is the correct length + if (end - begin == expectedLength) { + // length guarantees that this does not overflow, and value is at most type(uint160).max + (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end); + return (s, address(uint160(v))); + } else { + return (false, address(0)); + } + } + + function _tryParseChr(bytes1 chr) private pure returns (uint8) { + uint8 value = uint8(chr); + + // Try to parse `chr`: + // - Case 1: [0-9] + // - Case 2: [a-f] + // - Case 3: [A-F] + // - otherwise not supported + unchecked { + if (value > 47 && value < 58) value -= 48; + else if (value > 96 && value < 103) value -= 87; + else if (value > 64 && value < 71) value -= 55; + else return type(uint8).max; + } + + 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. + * + * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the + * assembly block as such would prevent some optimizations. + */ + 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(add(buffer, 0x20), offset)) + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/introspection/ERC165.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + */ +abstract contract ERC165 is IERC165 { + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/ERC721.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/ERC721.sol) + +pragma solidity ^0.8.20; + + + + + + + + +/** + * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC-721] Non-Fungible Token Standard, including + * the Metadata extension, but not including the Enumerable extension, which is available separately as + * {ERC721Enumerable}. + */ +abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors { + using Strings for uint256; + + // Token name + string private _name; + + // Token symbol + string private _symbol; + + mapping(uint256 tokenId => address) private _owners; + + mapping(address owner => uint256) private _balances; + + mapping(uint256 tokenId => address) private _tokenApprovals; + + mapping(address owner => mapping(address operator => bool)) private _operatorApprovals; + + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC721Metadata).interfaceId || + super.supportsInterface(interfaceId); + } + + /// @inheritdoc IERC721 + function balanceOf(address owner) public view virtual returns (uint256) { + if (owner == address(0)) { + revert ERC721InvalidOwner(address(0)); + } + return _balances[owner]; + } + + /// @inheritdoc IERC721 + function ownerOf(uint256 tokenId) public view virtual returns (address) { + return _requireOwned(tokenId); + } + + /// @inheritdoc IERC721Metadata + function name() public view virtual returns (string memory) { + return _name; + } + + /// @inheritdoc IERC721Metadata + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /// @inheritdoc IERC721Metadata + function tokenURI(uint256 tokenId) public view virtual returns (string memory) { + _requireOwned(tokenId); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, can be overridden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + /// @inheritdoc IERC721 + function approve(address to, uint256 tokenId) public virtual { + _approve(to, tokenId, _msgSender()); + } + + /// @inheritdoc IERC721 + function getApproved(uint256 tokenId) public view virtual returns (address) { + _requireOwned(tokenId); + + return _getApproved(tokenId); + } + + /// @inheritdoc IERC721 + function setApprovalForAll(address operator, bool approved) public virtual { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /// @inheritdoc IERC721 + function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { + return _operatorApprovals[owner][operator]; + } + + /// @inheritdoc IERC721 + function transferFrom(address from, address to, uint256 tokenId) public virtual { + if (to == address(0)) { + revert ERC721InvalidReceiver(address(0)); + } + // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists + // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. + address previousOwner = _update(to, tokenId, _msgSender()); + if (previousOwner != from) { + revert ERC721IncorrectOwner(from, tokenId, previousOwner); + } + } + + /// @inheritdoc IERC721 + function safeTransferFrom(address from, address to, uint256 tokenId) public { + safeTransferFrom(from, to, tokenId, ""); + } + + /// @inheritdoc IERC721 + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual { + transferFrom(from, to, tokenId); + ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data); + } + + /** + * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist + * + * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the + * core ERC-721 logic MUST be matched with the use of {_increaseBalance} to keep balances + * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by + * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`. + */ + function _ownerOf(uint256 tokenId) internal view virtual returns (address) { + return _owners[tokenId]; + } + + /** + * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted. + */ + function _getApproved(uint256 tokenId) internal view virtual returns (address) { + return _tokenApprovals[tokenId]; + } + + /** + * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in + * particular (ignoring whether it is owned by `owner`). + * + * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this + * assumption. + */ + function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) { + return + spender != address(0) && + (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender); + } + + /** + * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner. + * Reverts if: + * - `spender` does not have approval from `owner` for `tokenId`. + * - `spender` does not have approval to manage all of `owner`'s assets. + * + * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this + * assumption. + */ + function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual { + if (!_isAuthorized(owner, spender, tokenId)) { + if (owner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } else { + revert ERC721InsufficientApproval(spender, tokenId); + } + } + } + + /** + * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override. + * + * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that + * a uint256 would ever overflow from increments when these increments are bounded to uint128 values. + * + * WARNING: Increasing an account's balance using this function tends to be paired with an override of the + * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership + * remain consistent with one another. + */ + function _increaseBalance(address account, uint128 value) internal virtual { + unchecked { + _balances[account] += value; + } + } + + /** + * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner + * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update. + * + * The `auth` argument is optional. If the value passed is non 0, then this function will check that + * `auth` is either the owner of the token, or approved to operate on the token (by the owner). + * + * Emits a {Transfer} event. + * + * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}. + */ + function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) { + address from = _ownerOf(tokenId); + + // Perform (optional) operator check + if (auth != address(0)) { + _checkAuthorized(from, auth, tokenId); + } + + // Execute the update + if (from != address(0)) { + // Clear approval. No need to re-authorize or emit the Approval event + _approve(address(0), tokenId, address(0), false); + + unchecked { + _balances[from] -= 1; + } + } + + if (to != address(0)) { + unchecked { + _balances[to] += 1; + } + } + + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + + return from; + } + + /** + * @dev Mints `tokenId` and transfers it to `to`. + * + * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible + * + * Requirements: + * + * - `tokenId` must not exist. + * - `to` cannot be the zero address. + * + * Emits a {Transfer} event. + */ + function _mint(address to, uint256 tokenId) internal { + if (to == address(0)) { + revert ERC721InvalidReceiver(address(0)); + } + address previousOwner = _update(to, tokenId, address(0)); + if (previousOwner != address(0)) { + revert ERC721InvalidSender(address(0)); + } + } + + /** + * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance. + * + * Requirements: + * + * - `tokenId` must not exist. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeMint(address to, uint256 tokenId) internal { + _safeMint(to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { + _mint(to, tokenId); + ERC721Utils.checkOnERC721Received(_msgSender(), address(0), to, tokenId, data); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * This is an internal function that does not check if the sender is authorized to operate on the token. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal { + address previousOwner = _update(address(0), tokenId, address(0)); + if (previousOwner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } + } + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * + * Emits a {Transfer} event. + */ + function _transfer(address from, address to, uint256 tokenId) internal { + if (to == address(0)) { + revert ERC721InvalidReceiver(address(0)); + } + address previousOwner = _update(to, tokenId, address(0)); + if (previousOwner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } else if (previousOwner != from) { + revert ERC721IncorrectOwner(from, tokenId, previousOwner); + } + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients + * are aware of the ERC-721 standard to prevent tokens from being forever locked. + * + * `data` is additional data, it has no specified format and it is sent in call to `to`. + * + * This internal function is like {safeTransferFrom} in the sense that it invokes + * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g. + * implement alternative mechanisms to perform token transfer, such as signature-based. + * + * Requirements: + * + * - `tokenId` token must exist and be owned by `from`. + * - `to` cannot be the zero address. + * - `from` cannot be the zero address. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeTransfer(address from, address to, uint256 tokenId) internal { + _safeTransfer(from, to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual { + _transfer(from, to, tokenId); + ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data); + } + + /** + * @dev Approve `to` to operate on `tokenId` + * + * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is + * either the owner of the token, or approved to operate on all tokens held by this owner. + * + * Emits an {Approval} event. + * + * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. + */ + function _approve(address to, uint256 tokenId, address auth) internal { + _approve(to, tokenId, auth, true); + } + + /** + * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not + * emitted in the context of transfers. + */ + function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual { + // Avoid reading the owner unless necessary + if (emitEvent || auth != address(0)) { + address owner = _requireOwned(tokenId); + + // We do not use _isAuthorized because single-token approvals should not be able to call approve + if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) { + revert ERC721InvalidApprover(auth); + } + + if (emitEvent) { + emit Approval(owner, to, tokenId); + } + } + + _tokenApprovals[tokenId] = to; + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Requirements: + * - operator can't be the address zero. + * + * Emits an {ApprovalForAll} event. + */ + function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { + if (operator == address(0)) { + revert ERC721InvalidOperator(operator); + } + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned). + * Returns the owner. + * + * Overrides to ownership logic should be done to {_ownerOf}. + */ + function _requireOwned(uint256 tokenId) internal view returns (address) { + address owner = _ownerOf(tokenId); + if (owner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } + return owner; + } +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/extensions/IERC721Enumerable.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/IERC721Enumerable.sol) + +pragma solidity >=0.6.2; + + +/** + * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Enumerable is IERC721 { + /** + * @dev Returns the total amount of tokens stored by the contract. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns a token ID owned by `owner` at a given `index` of its token list. + * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /** + * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. + * Use along with {totalSupply} to enumerate all tokens. + */ + function tokenByIndex(uint256 index) external view returns (uint256); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/extensions/ERC721Enumerable.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/ERC721Enumerable.sol) + +pragma solidity ^0.8.20; + + + + +/** + * @dev This implements an optional extension of {ERC721} defined in the ERC that adds enumerability + * of all the token ids in the contract as well as all token ids owned by each account. + * + * CAUTION: {ERC721} extensions that implement custom `balanceOf` logic, such as {ERC721Consecutive}, + * interfere with enumerability and should not be used together with {ERC721Enumerable}. + */ +abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { + mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens; + mapping(uint256 tokenId => uint256) private _ownedTokensIndex; + + uint256[] private _allTokens; + mapping(uint256 tokenId => uint256) private _allTokensIndex; + + /** + * @dev An `owner`'s token query was out of bounds for `index`. + * + * NOTE: The owner being `address(0)` indicates a global out of bounds index. + */ + error ERC721OutOfBoundsIndex(address owner, uint256 index); + + /** + * @dev Batch mint is not allowed. + */ + error ERC721EnumerableForbiddenBatchMint(); + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /// @inheritdoc IERC721Enumerable + function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) { + if (index >= balanceOf(owner)) { + revert ERC721OutOfBoundsIndex(owner, index); + } + return _ownedTokens[owner][index]; + } + + /// @inheritdoc IERC721Enumerable + function totalSupply() public view virtual returns (uint256) { + return _allTokens.length; + } + + /// @inheritdoc IERC721Enumerable + function tokenByIndex(uint256 index) public view virtual returns (uint256) { + if (index >= totalSupply()) { + revert ERC721OutOfBoundsIndex(address(0), index); + } + return _allTokens[index]; + } + + /// @inheritdoc ERC721 + function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) { + address previousOwner = super._update(to, tokenId, auth); + + if (previousOwner == address(0)) { + _addTokenToAllTokensEnumeration(tokenId); + } else if (previousOwner != to) { + _removeTokenFromOwnerEnumeration(previousOwner, tokenId); + } + if (to == address(0)) { + _removeTokenFromAllTokensEnumeration(tokenId); + } else if (previousOwner != to) { + _addTokenToOwnerEnumeration(to, tokenId); + } + + return previousOwner; + } + + /** + * @dev Private function to add a token to this extension's ownership-tracking data structures. + * @param to address representing the new owner of the given token ID + * @param tokenId uint256 ID of the token to be added to the tokens list of the given address + */ + function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { + uint256 length = balanceOf(to) - 1; + _ownedTokens[to][length] = tokenId; + _ownedTokensIndex[tokenId] = length; + } + + /** + * @dev Private function to add a token to this extension's token tracking data structures. + * @param tokenId uint256 ID of the token to be added to the tokens list + */ + function _addTokenToAllTokensEnumeration(uint256 tokenId) private { + _allTokensIndex[tokenId] = _allTokens.length; + _allTokens.push(tokenId); + } + + /** + * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that + * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for + * gas optimizations e.g. when performing a transfer operation (avoiding double writes). + * This has O(1) time complexity, but alters the order of the _ownedTokens array. + * @param from address representing the previous owner of the given token ID + * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address + */ + function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { + // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = balanceOf(from); + uint256 tokenIndex = _ownedTokensIndex[tokenId]; + + mapping(uint256 index => uint256) storage _ownedTokensByOwner = _ownedTokens[from]; + + // When the token to delete is the last token, the swap operation is unnecessary + if (tokenIndex != lastTokenIndex) { + uint256 lastTokenId = _ownedTokensByOwner[lastTokenIndex]; + + _ownedTokensByOwner[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + } + + // This also deletes the contents at the last position of the array + delete _ownedTokensIndex[tokenId]; + delete _ownedTokensByOwner[lastTokenIndex]; + } + + /** + * @dev Private function to remove a token from this extension's token tracking data structures. + * This has O(1) time complexity, but alters the order of the _allTokens array. + * @param tokenId uint256 ID of the token to be removed from the tokens list + */ + function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { + // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = _allTokens.length - 1; + uint256 tokenIndex = _allTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so + // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding + // an 'if' statement (like in _removeTokenFromOwnerEnumeration) + uint256 lastTokenId = _allTokens[lastTokenIndex]; + + _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + + // This also deletes the contents at the last position of the array + delete _allTokensIndex[tokenId]; + _allTokens.pop(); + } + + /** + * See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch + */ + function _increaseBalance(address account, uint128 amount) internal virtual override { + if (amount > 0) { + revert ERC721EnumerableForbiddenBatchMint(); + } + super._increaseBalance(account, amount); + } +} + +// File: contracts/NFTNumbered.sol + + +pragma solidity ^0.8.27; + + + + +/// @title NFTNumbered +/// @notice ERC721 contract with sequential variants for immutable metadata for minted tokens, allowing to change metadata for future tokens. +/// @dev Non-upgradeable. Minter address is settable by owner. Global sequential token ID. +contract NFTNumbered is ERC721Enumerable, Ownable { + address public minter; + bool public mintingLocked; + TokenInfo[] private tokenInfos; + uint public nextTokenId = 1; + + struct TokenInfo { + uint tokenId; + string tokenUri; + } + + constructor( + string memory _name, + string memory _symbol, + string memory _tokenUri + ) ERC721(_name, _symbol) Ownable(msg.sender) { + minter = msg.sender; + mintingLocked = false; + setNextTokenURI(_tokenUri); + } + + event MinterUpdated(address indexed newMinter); + event MintingLocked(); + event NextTokenURI(uint nextTokenId, string nextTokenUri); + + /// @notice Updates the minter address. + function setMinter(address newMinter) external onlyOwner whenNotLocked { + minter = newMinter; + emit MinterUpdated(newMinter); + } + + /// @notice Permanently disable minting and changes. + function lockMintingPermanently() external onlyOwner { + mintingLocked = true; + emit MintingLocked(); + } + + modifier whenNotLocked() { + require(!mintingLocked, "Contract permanently locked for changes and minting"); + _; + } + + /// @notice Sets tokenUri for future mints only. + function setNextTokenURI(string memory newTokenUri) public onlyOwner whenNotLocked { + uint len = tokenInfos.length; + TokenInfo memory newInfo = TokenInfo({tokenId: nextTokenId, tokenUri: newTokenUri}); + if (len == 0 || tokenInfos[len - 1].tokenId < nextTokenId) { // add + tokenInfos.push(newInfo); + } else { // replace + tokenInfos[len - 1] = newInfo; + } + emit NextTokenURI(nextTokenId, newTokenUri); + } + + /// @notice metadata URI of the token that will be minted next + function nextTokenURI() external view returns (string memory) { + return tokenInfos[tokenInfos.length - 1].tokenUri; + } + + /// @notice Mints a new token. + /// @param to The recipient of the token. + function mint(address to) external whenNotLocked { + require(msg.sender == owner() || msg.sender == minter, "Caller must be the owner or minter"); + require(to != address(0), "Cannot mint to zero address"); + uint tokenId = nextTokenId++; + _safeMint(to, tokenId); + } + + /// @notice Burn owned token. + function burn(uint tokenId) external { + _burn(tokenId); + } + + /// @notice prohibit approvals + // address to, uint256 tokenId + function approve(address, uint256) public virtual override (ERC721, IERC721) { + revert("Soulbound token: approvals prohibited"); + } + + /// @notice prohibit approvals + // address operator, bool approved + function setApprovalForAll(address, bool) public virtual override (ERC721, IERC721) { + revert("Soulbound token: approvals prohibited"); + } + + /// @notice Limits ownership to 1 token. + function _update(address to, uint tokenId, address auth) internal virtual override returns (address) { + require(to == address(0) || balanceOf(to) == 0, "Soulbound token: only 1 per address"); + require(to == address(0) || _ownerOf(tokenId) == address(0), "Soulbound token: transfers prohibited"); + return super._update(to, tokenId, auth); + } + + /// @notice Returns embedded JSON metadata URI. + /// @param id Token ID. + function tokenURI(uint id) public view virtual override returns (string memory) { + _requireOwned(id); + uint len = tokenInfos.length; + for (uint i = len; i > 0; ) { + unchecked { i--; } + if (tokenInfos[i].tokenId > id) continue; + return tokenInfos[i].tokenUri; + } + revert("Unknown token ID"); + } + + /// @notice Withdraw any accidental ETH. + function withdraw() external onlyOwner { + (bool success, ) = payable(owner()).call{value: address(this).balance}(""); + require(success, "Withdraw failed"); + } +} + +// File: contracts/NFTMinter.sol + + +pragma solidity ^0.8.27; + + + + +contract NFTMinter is Ownable, Pausable { + NFTNumbered public nft; + uint public mintStartTime; + uint public mintEndTime; + uint public mintCount; + + /// @param _mintEndTime Unix timestamp when minting ends, 0 to mint without time limit. + constructor(address _nftAddress, uint _mintStartTime, uint _mintEndTime, bool _paused) Ownable(msg.sender) { + _setNFT(_nftAddress); + require(_mintEndTime == 0 || _mintEndTime > _mintStartTime, "Mint end time is before start time"); + require(_mintEndTime == 0 || _mintEndTime > block.timestamp, "Mint end time is in the past"); + mintStartTime = _mintStartTime; + mintEndTime = _mintEndTime; + if (_paused) _pause(); + } + + event NFTUpdated(address indexed newNFT); + event Minted(address indexed to, uint count); + event MintStartUpdated(uint newTime); + event MintEndUpdated(uint newTime); + event MintCountReset(uint oldCount); + + /// @notice Allows anyone to mint NFT for free (gas only). Any mint restrictions other than not paused or mint time must be in NFT contract + function mint() external whenNotPaused { + require(mintEndTime == 0 || mintEndTime > block.timestamp, "Minting ended"); + require(mintStartTime <= block.timestamp, "Minting not started"); + nft.mint(msg.sender); + mintCount++; + emit Minted(msg.sender, mintCount); + } + + /// @notice Update the target NFT contract. + /// @param newNFT The new NFT contract address. + function setNFT(address newNFT) external onlyOwner { + _setNFT(newNFT); + emit NFTUpdated(newNFT); + } + + function _setNFT(address _nft) internal { + require(_nft != address(0), "NFT contract address is 0"); + uint codeSize; + assembly { codeSize := extcodesize(_nft) } + require(codeSize > 0, "Not a contract address"); + nft = NFTNumbered(_nft); + } + + /// @notice Reset mint counter. + function resetMintCount() external onlyOwner { + uint old = mintCount; + mintCount = 0; + emit MintCountReset(old); + } + + /// @notice Update the mint start timestamp. + /// @param newTime The new Unix timestamp when minting ends. + function setMintStartTime(uint256 newTime) external onlyOwner { + require(mintEndTime == 0 || newTime < mintEndTime, "Mint end time is before start time"); + mintStartTime = newTime; + emit MintStartUpdated(newTime); + } + + /// @notice Update the mint end timestamp. + /// @param newTime The new Unix timestamp when minting ends, 0 to mint without time limit. + function setMintEndTime(uint256 newTime) external onlyOwner { + require(newTime == 0 || newTime > mintStartTime, "Mint end time is before start time"); + require(newTime == 0 || newTime > block.timestamp, "Mint end time is in the past"); + mintEndTime = newTime; + emit MintEndUpdated(newTime); + } + + /// @notice Pause minting. + function pause() external onlyOwner { + _pause(); + } + + /// @notice Unpause minting. + function unpause() external onlyOwner { + _unpause(); + } + + /// @notice Withdraw any accidental ETH. + function withdraw() external onlyOwner { + payable(owner()).transfer(address(this).balance); + } +} diff --git a/eth/nft/contracts/NFTNumbered.sol b/eth/nft/contracts/NFTNumbered.sol new file mode 100644 index 0000000000..7635d776b2 --- /dev/null +++ b/eth/nft/contracts/NFTNumbered.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts@5.4.0/token/ERC721/extensions/ERC721Enumerable.sol"; +import {IERC721} from "@openzeppelin/contracts@5.4.0/token/ERC721/IERC721.sol"; +import "@openzeppelin/contracts@5.4.0/access/Ownable.sol"; + +/// @title NFTNumbered +/// @notice ERC721 contract with sequential variants for immutable metadata for minted tokens, allowing to change metadata for future tokens. +/// @dev Non-upgradeable. Minter address is settable by owner. Global sequential token ID. +contract NFTNumbered is ERC721Enumerable, Ownable { + address public minter; + bool public mintingLocked; + TokenInfo[] private tokenInfos; + uint public nextTokenId = 1; + + struct TokenInfo { + uint tokenId; + string tokenUri; + } + + constructor( + string memory _name, + string memory _symbol, + string memory _tokenUri + ) ERC721(_name, _symbol) Ownable(msg.sender) { + minter = msg.sender; + mintingLocked = false; + setNextTokenURI(_tokenUri); + } + + event MinterUpdated(address indexed newMinter); + event MintingLocked(); + event NextTokenURI(uint nextTokenId, string nextTokenUri); + + /// @notice Updates the minter address. + function setMinter(address newMinter) external onlyOwner whenNotLocked { + minter = newMinter; + emit MinterUpdated(newMinter); + } + + /// @notice Permanently disable minting and changes. + function lockMintingPermanently() external onlyOwner { + mintingLocked = true; + emit MintingLocked(); + } + + modifier whenNotLocked() { + require(!mintingLocked, "Contract permanently locked for changes and minting"); + _; + } + + /// @notice Sets tokenUri for future mints only. + function setNextTokenURI(string memory newTokenUri) public onlyOwner whenNotLocked { + uint len = tokenInfos.length; + TokenInfo memory newInfo = TokenInfo({tokenId: nextTokenId, tokenUri: newTokenUri}); + if (len == 0 || tokenInfos[len - 1].tokenId < nextTokenId) { // add + tokenInfos.push(newInfo); + } else { // replace + tokenInfos[len - 1] = newInfo; + } + emit NextTokenURI(nextTokenId, newTokenUri); + } + + /// @notice metadata URI of the token that will be minted next + function nextTokenURI() external view returns (string memory) { + return tokenInfos[tokenInfos.length - 1].tokenUri; + } + + /// @notice Mints a new token. + /// @param to The recipient of the token. + function mint(address to) external whenNotLocked { + require(msg.sender == owner() || msg.sender == minter, "Caller must be the owner or minter"); + require(to != address(0), "Cannot mint to zero address"); + uint tokenId = nextTokenId++; + _safeMint(to, tokenId); + } + + /// @notice Burn owned token. + function burn(uint tokenId) external { + _burn(tokenId); + } + + /// @notice prohibit approvals + // address to, uint256 tokenId + function approve(address, uint256) public virtual override (ERC721, IERC721) { + revert("Soulbound token: approvals prohibited"); + } + + /// @notice prohibit approvals + // address operator, bool approved + function setApprovalForAll(address, bool) public virtual override (ERC721, IERC721) { + revert("Soulbound token: approvals prohibited"); + } + + /// @notice Limits ownership to 1 token. + function _update(address to, uint tokenId, address auth) internal virtual override returns (address) { + require(to == address(0) || balanceOf(to) == 0, "Soulbound token: only 1 per address"); + require(to == address(0) || _ownerOf(tokenId) == address(0), "Soulbound token: transfers prohibited"); + return super._update(to, tokenId, auth); + } + + /// @notice Returns embedded JSON metadata URI. + /// @param id Token ID. + function tokenURI(uint id) public view virtual override returns (string memory) { + _requireOwned(id); + uint len = tokenInfos.length; + for (uint i = len; i > 0; ) { + unchecked { i--; } + if (tokenInfos[i].tokenId > id) continue; + return tokenInfos[i].tokenUri; + } + revert("Unknown token ID"); + } + + /// @notice Withdraw any accidental ETH. + function withdraw() external onlyOwner { + (bool success, ) = payable(owner()).call{value: address(this).balance}(""); + require(success, "Withdraw failed"); + } +} diff --git a/eth/nft/contracts/NFTNumbered_flattened.sol b/eth/nft/contracts/NFTNumbered_flattened.sol new file mode 100644 index 0000000000..9a2ac8b0ac --- /dev/null +++ b/eth/nft/contracts/NFTNumbered_flattened.sol @@ -0,0 +1,3892 @@ +// SPDX-License-Identifier: MIT + +// File: @openzeppelin/contracts@5.4.0/utils/introspection/IERC165.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol) + +pragma solidity >=0.4.16; + +/** + * @dev Interface of the ERC-165 standard, as defined in the + * https://eips.ethereum.org/EIPS/eip-165[ERC]. + * + * Implementers can declare support of contract interfaces, which can then be + * queried by others ({ERC165Checker}). + * + * For an implementation, see {ERC165}. + */ +interface IERC165 { + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/IERC721.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721.sol) + +pragma solidity >=0.6.2; + + +/** + * @dev Required interface of an ERC-721 compliant contract. + */ +interface IERC721 is IERC165 { + /** + * @dev Emitted when `tokenId` token is transferred from `from` to `to`. + */ + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. + */ + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + + /** + * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. + */ + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + + /** + * @dev Returns the number of tokens in ``owner``'s account. + */ + function balanceOf(address owner) external view returns (uint256 balance); + + /** + * @dev Returns the owner of the `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function ownerOf(uint256 tokenId) external view returns (address owner); + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon + * a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients + * are aware of the ERC-721 protocol to prevent tokens from being forever locked. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must exist and be owned by `from`. + * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or + * {setApprovalForAll}. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon + * a safe transfer. + * + * Emits a {Transfer} event. + */ + function safeTransferFrom(address from, address to, uint256 tokenId) external; + + /** + * @dev Transfers `tokenId` token from `from` to `to`. + * + * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721 + * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must + * understand this adds an external call which potentially creates a reentrancy vulnerability. + * + * Requirements: + * + * - `from` cannot be the zero address. + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. + * + * Emits a {Transfer} event. + */ + function transferFrom(address from, address to, uint256 tokenId) external; + + /** + * @dev Gives permission to `to` to transfer `tokenId` token to another account. + * The approval is cleared when the token is transferred. + * + * Only a single account can be approved at a time, so approving the zero address clears previous approvals. + * + * Requirements: + * + * - The caller must own the token or be an approved operator. + * - `tokenId` must exist. + * + * Emits an {Approval} event. + */ + function approve(address to, uint256 tokenId) external; + + /** + * @dev Approve or remove `operator` as an operator for the caller. + * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. + * + * Requirements: + * + * - The `operator` cannot be the address zero. + * + * Emits an {ApprovalForAll} event. + */ + function setApprovalForAll(address operator, bool approved) external; + + /** + * @dev Returns the account approved for `tokenId` token. + * + * Requirements: + * + * - `tokenId` must exist. + */ + function getApproved(uint256 tokenId) external view returns (address operator); + + /** + * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. + * + * See {setApprovalForAll} + */ + function isApprovedForAll(address owner, address operator) external view returns (bool); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/extensions/IERC721Metadata.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/IERC721Metadata.sol) + +pragma solidity >=0.6.2; + + +/** + * @title ERC-721 Non-Fungible Token Standard, optional metadata extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Metadata is IERC721 { + /** + * @dev Returns the token collection name. + */ + function name() external view returns (string memory); + + /** + * @dev Returns the token collection symbol. + */ + function symbol() external view returns (string memory); + + /** + * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. + */ + function tokenURI(uint256 tokenId) external view returns (string memory); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/IERC721Receiver.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/IERC721Receiver.sol) + +pragma solidity >=0.5.0; + +/** + * @title ERC-721 token receiver interface + * @dev Interface for any contract that wants to support safeTransfers + * from ERC-721 asset contracts. + */ +interface IERC721Receiver { + /** + * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} + * by `operator` from `from`, this function is called. + * + * It must return its Solidity selector to confirm the token transfer. + * If any other value is returned or the interface is not implemented by the recipient, the transfer will be + * reverted. + * + * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. + */ + function onERC721Received( + address operator, + address from, + uint256 tokenId, + bytes calldata data + ) external returns (bytes4); +} + +// File: @openzeppelin/contracts@5.4.0/interfaces/draft-IERC6093.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol) +pragma solidity >=0.8.4; + +/** + * @dev Standard ERC-20 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens. + */ +interface IERC20Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC20InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC20InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` is allowed to operate with. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC20InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `spender` to be approved. Used in approvals. + * @param spender Address that may be allowed to operate on tokens without being their owner. + */ + error ERC20InvalidSpender(address spender); +} + +/** + * @dev Standard ERC-721 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens. + */ +interface IERC721Errors { + /** + * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20. + * Used in balance queries. + * @param owner Address of the current owner of a token. + */ + error ERC721InvalidOwner(address owner); + + /** + * @dev Indicates a `tokenId` whose `owner` is the zero address. + * @param tokenId Identifier number of a token. + */ + error ERC721NonexistentToken(uint256 tokenId); + + /** + * @dev Indicates an error related to the ownership over a particular token. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param tokenId Identifier number of a token. + * @param owner Address of the current owner of a token. + */ + error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC721InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC721InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param tokenId Identifier number of a token. + */ + error ERC721InsufficientApproval(address operator, uint256 tokenId); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC721InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC721InvalidOperator(address operator); +} + +/** + * @dev Standard ERC-1155 Errors + * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens. + */ +interface IERC1155Errors { + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + * @param tokenId Identifier number of a token. + */ + error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); + + /** + * @dev Indicates a failure with the token `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + */ + error ERC1155InvalidSender(address sender); + + /** + * @dev Indicates a failure with the token `receiver`. Used in transfers. + * @param receiver Address to which tokens are being transferred. + */ + error ERC1155InvalidReceiver(address receiver); + + /** + * @dev Indicates a failure with the `operator`’s approval. Used in transfers. + * @param operator Address that may be allowed to operate on tokens without being their owner. + * @param owner Address of the current owner of a token. + */ + error ERC1155MissingApprovalForAll(address operator, address owner); + + /** + * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. + * @param approver Address initiating an approval operation. + */ + error ERC1155InvalidApprover(address approver); + + /** + * @dev Indicates a failure with the `operator` to be approved. Used in approvals. + * @param operator Address that may be allowed to operate on tokens without being their owner. + */ + error ERC1155InvalidOperator(address operator); + + /** + * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. + * Used in batch transfers. + * @param idsLength Length of the array of token identifiers + * @param valuesLength Length of the array of token amounts + */ + error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/utils/ERC721Utils.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/utils/ERC721Utils.sol) + +pragma solidity ^0.8.20; + + + +/** + * @dev Library that provide common ERC-721 utility functions. + * + * See https://eips.ethereum.org/EIPS/eip-721[ERC-721]. + * + * _Available since v5.1._ + */ +library ERC721Utils { + /** + * @dev Performs an acceptance check for the provided `operator` by calling {IERC721Receiver-onERC721Received} + * on the `to` address. The `operator` is generally the address that initiated the token transfer (i.e. `msg.sender`). + * + * The acceptance call is not executed and treated as a no-op if the target address doesn't contain code (i.e. an EOA). + * Otherwise, the recipient must implement {IERC721Receiver-onERC721Received} and return the acceptance magic value to accept + * the transfer. + */ + function checkOnERC721Received( + address operator, + address from, + address to, + uint256 tokenId, + bytes memory data + ) internal { + if (to.code.length > 0) { + try IERC721Receiver(to).onERC721Received(operator, from, tokenId, data) returns (bytes4 retval) { + if (retval != IERC721Receiver.onERC721Received.selector) { + // Token rejected + revert IERC721Errors.ERC721InvalidReceiver(to); + } + } catch (bytes memory reason) { + if (reason.length == 0) { + // non-IERC721Receiver implementer + revert IERC721Errors.ERC721InvalidReceiver(to); + } else { + assembly ("memory-safe") { + revert(add(reason, 0x20), mload(reason)) + } + } + } + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/Context.sol + + +// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Provides information about the current execution context, including the + * sender of the transaction and its data. While these are generally available + * via msg.sender and msg.data, they should not be accessed in such a direct + * manner, since when dealing with meta-transactions the account sending and + * paying for execution may not be the actual sender (as far as an application + * is concerned). + * + * This contract is only required for intermediate, library-like contracts. + */ +abstract contract Context { + function _msgSender() internal view virtual returns (address) { + return msg.sender; + } + + function _msgData() internal view virtual returns (bytes calldata) { + return msg.data; + } + + function _contextSuffixLength() internal view virtual returns (uint256) { + return 0; + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/Panic.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol) + +pragma solidity ^0.8.20; + +/** + * @dev Helper library for emitting standardized panic codes. + * + * ```solidity + * contract Example { + * using Panic for uint256; + * + * // Use any of the declared internal constants + * function foo() { Panic.GENERIC.panic(); } + * + * // Alternatively + * function foo() { Panic.panic(Panic.GENERIC); } + * } + * ``` + * + * Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil]. + * + * _Available since v5.1._ + */ +// slither-disable-next-line unused-state +library Panic { + /// @dev generic / unspecified error + uint256 internal constant GENERIC = 0x00; + /// @dev used by the assert() builtin + uint256 internal constant ASSERT = 0x01; + /// @dev arithmetic underflow or overflow + uint256 internal constant UNDER_OVERFLOW = 0x11; + /// @dev division or modulo by zero + uint256 internal constant DIVISION_BY_ZERO = 0x12; + /// @dev enum conversion error + uint256 internal constant ENUM_CONVERSION_ERROR = 0x21; + /// @dev invalid encoding in storage + uint256 internal constant STORAGE_ENCODING_ERROR = 0x22; + /// @dev empty array pop + uint256 internal constant EMPTY_ARRAY_POP = 0x31; + /// @dev array out of bounds access + uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32; + /// @dev resource error (too large allocation or too large array) + uint256 internal constant RESOURCE_ERROR = 0x41; + /// @dev calling invalid internal function + uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51; + + /// @dev Reverts with a panic code. Recommended to use with + /// the internal constants with predefined codes. + function panic(uint256 code) internal pure { + assembly ("memory-safe") { + mstore(0x00, 0x4e487b71) + mstore(0x20, code) + revert(0x1c, 0x24) + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/math/SafeCast.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol) +// This file was procedurally generated from scripts/generate/templates/SafeCast.js. + +pragma solidity ^0.8.20; + +/** + * @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow + * checks. + * + * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can + * easily result in undesired exploitation or bugs, since developers usually + * assume that overflows raise errors. `SafeCast` restores this intuition by + * reverting the transaction when such an operation overflows. + * + * Using this library instead of the unchecked operations eliminates an entire + * class of bugs, so it's recommended to use it always. + */ +library SafeCast { + /** + * @dev Value doesn't fit in an uint of `bits` size. + */ + error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); + + /** + * @dev An int value doesn't fit in an uint of `bits` size. + */ + error SafeCastOverflowedIntToUint(int256 value); + + /** + * @dev Value doesn't fit in an int of `bits` size. + */ + error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); + + /** + * @dev An uint value doesn't fit in an int of `bits` size. + */ + error SafeCastOverflowedUintToInt(uint256 value); + + /** + * @dev Returns the downcasted uint248 from uint256, reverting on + * overflow (when the input is greater than largest uint248). + * + * Counterpart to Solidity's `uint248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + */ + function toUint248(uint256 value) internal pure returns (uint248) { + if (value > type(uint248).max) { + revert SafeCastOverflowedUintDowncast(248, value); + } + return uint248(value); + } + + /** + * @dev Returns the downcasted uint240 from uint256, reverting on + * overflow (when the input is greater than largest uint240). + * + * Counterpart to Solidity's `uint240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + */ + function toUint240(uint256 value) internal pure returns (uint240) { + if (value > type(uint240).max) { + revert SafeCastOverflowedUintDowncast(240, value); + } + return uint240(value); + } + + /** + * @dev Returns the downcasted uint232 from uint256, reverting on + * overflow (when the input is greater than largest uint232). + * + * Counterpart to Solidity's `uint232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + */ + function toUint232(uint256 value) internal pure returns (uint232) { + if (value > type(uint232).max) { + revert SafeCastOverflowedUintDowncast(232, value); + } + return uint232(value); + } + + /** + * @dev Returns the downcasted uint224 from uint256, reverting on + * overflow (when the input is greater than largest uint224). + * + * Counterpart to Solidity's `uint224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toUint224(uint256 value) internal pure returns (uint224) { + if (value > type(uint224).max) { + revert SafeCastOverflowedUintDowncast(224, value); + } + return uint224(value); + } + + /** + * @dev Returns the downcasted uint216 from uint256, reverting on + * overflow (when the input is greater than largest uint216). + * + * Counterpart to Solidity's `uint216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + */ + function toUint216(uint256 value) internal pure returns (uint216) { + if (value > type(uint216).max) { + revert SafeCastOverflowedUintDowncast(216, value); + } + return uint216(value); + } + + /** + * @dev Returns the downcasted uint208 from uint256, reverting on + * overflow (when the input is greater than largest uint208). + * + * Counterpart to Solidity's `uint208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + */ + function toUint208(uint256 value) internal pure returns (uint208) { + if (value > type(uint208).max) { + revert SafeCastOverflowedUintDowncast(208, value); + } + return uint208(value); + } + + /** + * @dev Returns the downcasted uint200 from uint256, reverting on + * overflow (when the input is greater than largest uint200). + * + * Counterpart to Solidity's `uint200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + */ + function toUint200(uint256 value) internal pure returns (uint200) { + if (value > type(uint200).max) { + revert SafeCastOverflowedUintDowncast(200, value); + } + return uint200(value); + } + + /** + * @dev Returns the downcasted uint192 from uint256, reverting on + * overflow (when the input is greater than largest uint192). + * + * Counterpart to Solidity's `uint192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + */ + function toUint192(uint256 value) internal pure returns (uint192) { + if (value > type(uint192).max) { + revert SafeCastOverflowedUintDowncast(192, value); + } + return uint192(value); + } + + /** + * @dev Returns the downcasted uint184 from uint256, reverting on + * overflow (when the input is greater than largest uint184). + * + * Counterpart to Solidity's `uint184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + */ + function toUint184(uint256 value) internal pure returns (uint184) { + if (value > type(uint184).max) { + revert SafeCastOverflowedUintDowncast(184, value); + } + return uint184(value); + } + + /** + * @dev Returns the downcasted uint176 from uint256, reverting on + * overflow (when the input is greater than largest uint176). + * + * Counterpart to Solidity's `uint176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + */ + function toUint176(uint256 value) internal pure returns (uint176) { + if (value > type(uint176).max) { + revert SafeCastOverflowedUintDowncast(176, value); + } + return uint176(value); + } + + /** + * @dev Returns the downcasted uint168 from uint256, reverting on + * overflow (when the input is greater than largest uint168). + * + * Counterpart to Solidity's `uint168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + */ + function toUint168(uint256 value) internal pure returns (uint168) { + if (value > type(uint168).max) { + revert SafeCastOverflowedUintDowncast(168, value); + } + return uint168(value); + } + + /** + * @dev Returns the downcasted uint160 from uint256, reverting on + * overflow (when the input is greater than largest uint160). + * + * Counterpart to Solidity's `uint160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + */ + function toUint160(uint256 value) internal pure returns (uint160) { + if (value > type(uint160).max) { + revert SafeCastOverflowedUintDowncast(160, value); + } + return uint160(value); + } + + /** + * @dev Returns the downcasted uint152 from uint256, reverting on + * overflow (when the input is greater than largest uint152). + * + * Counterpart to Solidity's `uint152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + */ + function toUint152(uint256 value) internal pure returns (uint152) { + if (value > type(uint152).max) { + revert SafeCastOverflowedUintDowncast(152, value); + } + return uint152(value); + } + + /** + * @dev Returns the downcasted uint144 from uint256, reverting on + * overflow (when the input is greater than largest uint144). + * + * Counterpart to Solidity's `uint144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + */ + function toUint144(uint256 value) internal pure returns (uint144) { + if (value > type(uint144).max) { + revert SafeCastOverflowedUintDowncast(144, value); + } + return uint144(value); + } + + /** + * @dev Returns the downcasted uint136 from uint256, reverting on + * overflow (when the input is greater than largest uint136). + * + * Counterpart to Solidity's `uint136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + */ + function toUint136(uint256 value) internal pure returns (uint136) { + if (value > type(uint136).max) { + revert SafeCastOverflowedUintDowncast(136, value); + } + return uint136(value); + } + + /** + * @dev Returns the downcasted uint128 from uint256, reverting on + * overflow (when the input is greater than largest uint128). + * + * Counterpart to Solidity's `uint128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toUint128(uint256 value) internal pure returns (uint128) { + if (value > type(uint128).max) { + revert SafeCastOverflowedUintDowncast(128, value); + } + return uint128(value); + } + + /** + * @dev Returns the downcasted uint120 from uint256, reverting on + * overflow (when the input is greater than largest uint120). + * + * Counterpart to Solidity's `uint120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + */ + function toUint120(uint256 value) internal pure returns (uint120) { + if (value > type(uint120).max) { + revert SafeCastOverflowedUintDowncast(120, value); + } + return uint120(value); + } + + /** + * @dev Returns the downcasted uint112 from uint256, reverting on + * overflow (when the input is greater than largest uint112). + * + * Counterpart to Solidity's `uint112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + */ + function toUint112(uint256 value) internal pure returns (uint112) { + if (value > type(uint112).max) { + revert SafeCastOverflowedUintDowncast(112, value); + } + return uint112(value); + } + + /** + * @dev Returns the downcasted uint104 from uint256, reverting on + * overflow (when the input is greater than largest uint104). + * + * Counterpart to Solidity's `uint104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + */ + function toUint104(uint256 value) internal pure returns (uint104) { + if (value > type(uint104).max) { + revert SafeCastOverflowedUintDowncast(104, value); + } + return uint104(value); + } + + /** + * @dev Returns the downcasted uint96 from uint256, reverting on + * overflow (when the input is greater than largest uint96). + * + * Counterpart to Solidity's `uint96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toUint96(uint256 value) internal pure returns (uint96) { + if (value > type(uint96).max) { + revert SafeCastOverflowedUintDowncast(96, value); + } + return uint96(value); + } + + /** + * @dev Returns the downcasted uint88 from uint256, reverting on + * overflow (when the input is greater than largest uint88). + * + * Counterpart to Solidity's `uint88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + */ + function toUint88(uint256 value) internal pure returns (uint88) { + if (value > type(uint88).max) { + revert SafeCastOverflowedUintDowncast(88, value); + } + return uint88(value); + } + + /** + * @dev Returns the downcasted uint80 from uint256, reverting on + * overflow (when the input is greater than largest uint80). + * + * Counterpart to Solidity's `uint80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + */ + function toUint80(uint256 value) internal pure returns (uint80) { + if (value > type(uint80).max) { + revert SafeCastOverflowedUintDowncast(80, value); + } + return uint80(value); + } + + /** + * @dev Returns the downcasted uint72 from uint256, reverting on + * overflow (when the input is greater than largest uint72). + * + * Counterpart to Solidity's `uint72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + */ + function toUint72(uint256 value) internal pure returns (uint72) { + if (value > type(uint72).max) { + revert SafeCastOverflowedUintDowncast(72, value); + } + return uint72(value); + } + + /** + * @dev Returns the downcasted uint64 from uint256, reverting on + * overflow (when the input is greater than largest uint64). + * + * Counterpart to Solidity's `uint64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toUint64(uint256 value) internal pure returns (uint64) { + if (value > type(uint64).max) { + revert SafeCastOverflowedUintDowncast(64, value); + } + return uint64(value); + } + + /** + * @dev Returns the downcasted uint56 from uint256, reverting on + * overflow (when the input is greater than largest uint56). + * + * Counterpart to Solidity's `uint56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + */ + function toUint56(uint256 value) internal pure returns (uint56) { + if (value > type(uint56).max) { + revert SafeCastOverflowedUintDowncast(56, value); + } + return uint56(value); + } + + /** + * @dev Returns the downcasted uint48 from uint256, reverting on + * overflow (when the input is greater than largest uint48). + * + * Counterpart to Solidity's `uint48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + */ + function toUint48(uint256 value) internal pure returns (uint48) { + if (value > type(uint48).max) { + revert SafeCastOverflowedUintDowncast(48, value); + } + return uint48(value); + } + + /** + * @dev Returns the downcasted uint40 from uint256, reverting on + * overflow (when the input is greater than largest uint40). + * + * Counterpart to Solidity's `uint40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + */ + function toUint40(uint256 value) internal pure returns (uint40) { + if (value > type(uint40).max) { + revert SafeCastOverflowedUintDowncast(40, value); + } + return uint40(value); + } + + /** + * @dev Returns the downcasted uint32 from uint256, reverting on + * overflow (when the input is greater than largest uint32). + * + * Counterpart to Solidity's `uint32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toUint32(uint256 value) internal pure returns (uint32) { + if (value > type(uint32).max) { + revert SafeCastOverflowedUintDowncast(32, value); + } + return uint32(value); + } + + /** + * @dev Returns the downcasted uint24 from uint256, reverting on + * overflow (when the input is greater than largest uint24). + * + * Counterpart to Solidity's `uint24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + */ + function toUint24(uint256 value) internal pure returns (uint24) { + if (value > type(uint24).max) { + revert SafeCastOverflowedUintDowncast(24, value); + } + return uint24(value); + } + + /** + * @dev Returns the downcasted uint16 from uint256, reverting on + * overflow (when the input is greater than largest uint16). + * + * Counterpart to Solidity's `uint16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toUint16(uint256 value) internal pure returns (uint16) { + if (value > type(uint16).max) { + revert SafeCastOverflowedUintDowncast(16, value); + } + return uint16(value); + } + + /** + * @dev Returns the downcasted uint8 from uint256, reverting on + * overflow (when the input is greater than largest uint8). + * + * Counterpart to Solidity's `uint8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + */ + function toUint8(uint256 value) internal pure returns (uint8) { + if (value > type(uint8).max) { + revert SafeCastOverflowedUintDowncast(8, value); + } + return uint8(value); + } + + /** + * @dev Converts a signed int256 into an unsigned uint256. + * + * Requirements: + * + * - input must be greater than or equal to 0. + */ + function toUint256(int256 value) internal pure returns (uint256) { + if (value < 0) { + revert SafeCastOverflowedIntToUint(value); + } + return uint256(value); + } + + /** + * @dev Returns the downcasted int248 from int256, reverting on + * overflow (when the input is less than smallest int248 or + * greater than largest int248). + * + * Counterpart to Solidity's `int248` operator. + * + * Requirements: + * + * - input must fit into 248 bits + */ + function toInt248(int256 value) internal pure returns (int248 downcasted) { + downcasted = int248(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(248, value); + } + } + + /** + * @dev Returns the downcasted int240 from int256, reverting on + * overflow (when the input is less than smallest int240 or + * greater than largest int240). + * + * Counterpart to Solidity's `int240` operator. + * + * Requirements: + * + * - input must fit into 240 bits + */ + function toInt240(int256 value) internal pure returns (int240 downcasted) { + downcasted = int240(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(240, value); + } + } + + /** + * @dev Returns the downcasted int232 from int256, reverting on + * overflow (when the input is less than smallest int232 or + * greater than largest int232). + * + * Counterpart to Solidity's `int232` operator. + * + * Requirements: + * + * - input must fit into 232 bits + */ + function toInt232(int256 value) internal pure returns (int232 downcasted) { + downcasted = int232(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(232, value); + } + } + + /** + * @dev Returns the downcasted int224 from int256, reverting on + * overflow (when the input is less than smallest int224 or + * greater than largest int224). + * + * Counterpart to Solidity's `int224` operator. + * + * Requirements: + * + * - input must fit into 224 bits + */ + function toInt224(int256 value) internal pure returns (int224 downcasted) { + downcasted = int224(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(224, value); + } + } + + /** + * @dev Returns the downcasted int216 from int256, reverting on + * overflow (when the input is less than smallest int216 or + * greater than largest int216). + * + * Counterpart to Solidity's `int216` operator. + * + * Requirements: + * + * - input must fit into 216 bits + */ + function toInt216(int256 value) internal pure returns (int216 downcasted) { + downcasted = int216(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(216, value); + } + } + + /** + * @dev Returns the downcasted int208 from int256, reverting on + * overflow (when the input is less than smallest int208 or + * greater than largest int208). + * + * Counterpart to Solidity's `int208` operator. + * + * Requirements: + * + * - input must fit into 208 bits + */ + function toInt208(int256 value) internal pure returns (int208 downcasted) { + downcasted = int208(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(208, value); + } + } + + /** + * @dev Returns the downcasted int200 from int256, reverting on + * overflow (when the input is less than smallest int200 or + * greater than largest int200). + * + * Counterpart to Solidity's `int200` operator. + * + * Requirements: + * + * - input must fit into 200 bits + */ + function toInt200(int256 value) internal pure returns (int200 downcasted) { + downcasted = int200(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(200, value); + } + } + + /** + * @dev Returns the downcasted int192 from int256, reverting on + * overflow (when the input is less than smallest int192 or + * greater than largest int192). + * + * Counterpart to Solidity's `int192` operator. + * + * Requirements: + * + * - input must fit into 192 bits + */ + function toInt192(int256 value) internal pure returns (int192 downcasted) { + downcasted = int192(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(192, value); + } + } + + /** + * @dev Returns the downcasted int184 from int256, reverting on + * overflow (when the input is less than smallest int184 or + * greater than largest int184). + * + * Counterpart to Solidity's `int184` operator. + * + * Requirements: + * + * - input must fit into 184 bits + */ + function toInt184(int256 value) internal pure returns (int184 downcasted) { + downcasted = int184(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(184, value); + } + } + + /** + * @dev Returns the downcasted int176 from int256, reverting on + * overflow (when the input is less than smallest int176 or + * greater than largest int176). + * + * Counterpart to Solidity's `int176` operator. + * + * Requirements: + * + * - input must fit into 176 bits + */ + function toInt176(int256 value) internal pure returns (int176 downcasted) { + downcasted = int176(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(176, value); + } + } + + /** + * @dev Returns the downcasted int168 from int256, reverting on + * overflow (when the input is less than smallest int168 or + * greater than largest int168). + * + * Counterpart to Solidity's `int168` operator. + * + * Requirements: + * + * - input must fit into 168 bits + */ + function toInt168(int256 value) internal pure returns (int168 downcasted) { + downcasted = int168(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(168, value); + } + } + + /** + * @dev Returns the downcasted int160 from int256, reverting on + * overflow (when the input is less than smallest int160 or + * greater than largest int160). + * + * Counterpart to Solidity's `int160` operator. + * + * Requirements: + * + * - input must fit into 160 bits + */ + function toInt160(int256 value) internal pure returns (int160 downcasted) { + downcasted = int160(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(160, value); + } + } + + /** + * @dev Returns the downcasted int152 from int256, reverting on + * overflow (when the input is less than smallest int152 or + * greater than largest int152). + * + * Counterpart to Solidity's `int152` operator. + * + * Requirements: + * + * - input must fit into 152 bits + */ + function toInt152(int256 value) internal pure returns (int152 downcasted) { + downcasted = int152(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(152, value); + } + } + + /** + * @dev Returns the downcasted int144 from int256, reverting on + * overflow (when the input is less than smallest int144 or + * greater than largest int144). + * + * Counterpart to Solidity's `int144` operator. + * + * Requirements: + * + * - input must fit into 144 bits + */ + function toInt144(int256 value) internal pure returns (int144 downcasted) { + downcasted = int144(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(144, value); + } + } + + /** + * @dev Returns the downcasted int136 from int256, reverting on + * overflow (when the input is less than smallest int136 or + * greater than largest int136). + * + * Counterpart to Solidity's `int136` operator. + * + * Requirements: + * + * - input must fit into 136 bits + */ + function toInt136(int256 value) internal pure returns (int136 downcasted) { + downcasted = int136(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(136, value); + } + } + + /** + * @dev Returns the downcasted int128 from int256, reverting on + * overflow (when the input is less than smallest int128 or + * greater than largest int128). + * + * Counterpart to Solidity's `int128` operator. + * + * Requirements: + * + * - input must fit into 128 bits + */ + function toInt128(int256 value) internal pure returns (int128 downcasted) { + downcasted = int128(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(128, value); + } + } + + /** + * @dev Returns the downcasted int120 from int256, reverting on + * overflow (when the input is less than smallest int120 or + * greater than largest int120). + * + * Counterpart to Solidity's `int120` operator. + * + * Requirements: + * + * - input must fit into 120 bits + */ + function toInt120(int256 value) internal pure returns (int120 downcasted) { + downcasted = int120(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(120, value); + } + } + + /** + * @dev Returns the downcasted int112 from int256, reverting on + * overflow (when the input is less than smallest int112 or + * greater than largest int112). + * + * Counterpart to Solidity's `int112` operator. + * + * Requirements: + * + * - input must fit into 112 bits + */ + function toInt112(int256 value) internal pure returns (int112 downcasted) { + downcasted = int112(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(112, value); + } + } + + /** + * @dev Returns the downcasted int104 from int256, reverting on + * overflow (when the input is less than smallest int104 or + * greater than largest int104). + * + * Counterpart to Solidity's `int104` operator. + * + * Requirements: + * + * - input must fit into 104 bits + */ + function toInt104(int256 value) internal pure returns (int104 downcasted) { + downcasted = int104(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(104, value); + } + } + + /** + * @dev Returns the downcasted int96 from int256, reverting on + * overflow (when the input is less than smallest int96 or + * greater than largest int96). + * + * Counterpart to Solidity's `int96` operator. + * + * Requirements: + * + * - input must fit into 96 bits + */ + function toInt96(int256 value) internal pure returns (int96 downcasted) { + downcasted = int96(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(96, value); + } + } + + /** + * @dev Returns the downcasted int88 from int256, reverting on + * overflow (when the input is less than smallest int88 or + * greater than largest int88). + * + * Counterpart to Solidity's `int88` operator. + * + * Requirements: + * + * - input must fit into 88 bits + */ + function toInt88(int256 value) internal pure returns (int88 downcasted) { + downcasted = int88(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(88, value); + } + } + + /** + * @dev Returns the downcasted int80 from int256, reverting on + * overflow (when the input is less than smallest int80 or + * greater than largest int80). + * + * Counterpart to Solidity's `int80` operator. + * + * Requirements: + * + * - input must fit into 80 bits + */ + function toInt80(int256 value) internal pure returns (int80 downcasted) { + downcasted = int80(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(80, value); + } + } + + /** + * @dev Returns the downcasted int72 from int256, reverting on + * overflow (when the input is less than smallest int72 or + * greater than largest int72). + * + * Counterpart to Solidity's `int72` operator. + * + * Requirements: + * + * - input must fit into 72 bits + */ + function toInt72(int256 value) internal pure returns (int72 downcasted) { + downcasted = int72(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(72, value); + } + } + + /** + * @dev Returns the downcasted int64 from int256, reverting on + * overflow (when the input is less than smallest int64 or + * greater than largest int64). + * + * Counterpart to Solidity's `int64` operator. + * + * Requirements: + * + * - input must fit into 64 bits + */ + function toInt64(int256 value) internal pure returns (int64 downcasted) { + downcasted = int64(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(64, value); + } + } + + /** + * @dev Returns the downcasted int56 from int256, reverting on + * overflow (when the input is less than smallest int56 or + * greater than largest int56). + * + * Counterpart to Solidity's `int56` operator. + * + * Requirements: + * + * - input must fit into 56 bits + */ + function toInt56(int256 value) internal pure returns (int56 downcasted) { + downcasted = int56(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(56, value); + } + } + + /** + * @dev Returns the downcasted int48 from int256, reverting on + * overflow (when the input is less than smallest int48 or + * greater than largest int48). + * + * Counterpart to Solidity's `int48` operator. + * + * Requirements: + * + * - input must fit into 48 bits + */ + function toInt48(int256 value) internal pure returns (int48 downcasted) { + downcasted = int48(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(48, value); + } + } + + /** + * @dev Returns the downcasted int40 from int256, reverting on + * overflow (when the input is less than smallest int40 or + * greater than largest int40). + * + * Counterpart to Solidity's `int40` operator. + * + * Requirements: + * + * - input must fit into 40 bits + */ + function toInt40(int256 value) internal pure returns (int40 downcasted) { + downcasted = int40(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(40, value); + } + } + + /** + * @dev Returns the downcasted int32 from int256, reverting on + * overflow (when the input is less than smallest int32 or + * greater than largest int32). + * + * Counterpart to Solidity's `int32` operator. + * + * Requirements: + * + * - input must fit into 32 bits + */ + function toInt32(int256 value) internal pure returns (int32 downcasted) { + downcasted = int32(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(32, value); + } + } + + /** + * @dev Returns the downcasted int24 from int256, reverting on + * overflow (when the input is less than smallest int24 or + * greater than largest int24). + * + * Counterpart to Solidity's `int24` operator. + * + * Requirements: + * + * - input must fit into 24 bits + */ + function toInt24(int256 value) internal pure returns (int24 downcasted) { + downcasted = int24(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(24, value); + } + } + + /** + * @dev Returns the downcasted int16 from int256, reverting on + * overflow (when the input is less than smallest int16 or + * greater than largest int16). + * + * Counterpart to Solidity's `int16` operator. + * + * Requirements: + * + * - input must fit into 16 bits + */ + function toInt16(int256 value) internal pure returns (int16 downcasted) { + downcasted = int16(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(16, value); + } + } + + /** + * @dev Returns the downcasted int8 from int256, reverting on + * overflow (when the input is less than smallest int8 or + * greater than largest int8). + * + * Counterpart to Solidity's `int8` operator. + * + * Requirements: + * + * - input must fit into 8 bits + */ + function toInt8(int256 value) internal pure returns (int8 downcasted) { + downcasted = int8(value); + if (downcasted != value) { + revert SafeCastOverflowedIntDowncast(8, value); + } + } + + /** + * @dev Converts an unsigned uint256 into a signed int256. + * + * Requirements: + * + * - input must be less than or equal to maxInt256. + */ + function toInt256(uint256 value) internal pure returns (int256) { + // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive + if (value > uint256(type(int256).max)) { + revert SafeCastOverflowedUintToInt(value); + } + return int256(value); + } + + /** + * @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump. + */ + function toUint(bool b) internal pure returns (uint256 u) { + assembly ("memory-safe") { + u := iszero(iszero(b)) + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/math/Math.sol + + +// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol) + +pragma solidity ^0.8.20; + + + +/** + * @dev Standard math utilities missing in the Solidity language. + */ +library Math { + enum Rounding { + Floor, // Toward negative infinity + Ceil, // Toward positive infinity + Trunc, // Toward zero + Expand // Away from zero + } + + /** + * @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; + success = c >= a; + result = c * SafeCast.toUint(success); + } + } + + /** + * @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 { + uint256 c = a - b; + success = c <= a; + result = c * SafeCast.toUint(success); + } + } + + /** + * @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 { + uint256 c = a * b; + 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); + } + } + + /** + * @dev Returns the division of two unsigned integers, with a success flag (no division by zero). + */ + function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { + unchecked { + success = b > 0; + assembly ("memory-safe") { + // The `DIV` opcode returns zero when the denominator is 0. + result := div(a, b) + } + } + } + + /** + * @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero). + */ + function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) { + unchecked { + 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. + * + * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone. + * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute + * one branch when needed, making this function more expensive. + */ + function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) { + unchecked { + // branchless ternary works because: + // b ^ (a ^ b) == a + // b ^ 0 == b + return b ^ ((a ^ b) * SafeCast.toUint(condition)); + } + } + + /** + * @dev Returns the largest of two numbers. + */ + function max(uint256 a, uint256 b) internal pure returns (uint256) { + return ternary(a > b, a, b); + } + + /** + * @dev Returns the smallest of two numbers. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return ternary(a < b, a, b); + } + + /** + * @dev Returns the average of two numbers. The result is rounded towards + * zero. + */ + function average(uint256 a, uint256 b) internal pure returns (uint256) { + // (a + b) / 2 can overflow. + return (a & b) + (a ^ b) / 2; + } + + /** + * @dev Returns the ceiling of the division of two numbers. + * + * This differs from standard division with `/` in that it rounds towards infinity instead + * of rounding towards zero. + */ + function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { + if (b == 0) { + // Guarantee the same behavior as in a regular Solidity division. + Panic.panic(Panic.DIVISION_BY_ZERO); + } + + // The following calculation ensures accurate ceiling division without overflow. + // Since a is non-zero, (a - 1) / b will not overflow. + // The largest possible result occurs when (a - 1) / b is type(uint256).max, + // but the largest value we can obtain is type(uint256).max - 1, which happens + // when a = type(uint256).max and b = 1. + unchecked { + return SafeCast.toUint(a > 0) * ((a - 1) / b + 1); + } + } + + /** + * @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or + * denominator == 0. + * + * Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by + * Uniswap Labs also under MIT license. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { + unchecked { + (uint256 high, uint256 low) = mul512(x, y); + + // Handle non-overflow cases, 256 by 256 division. + 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 low / denominator; + } + + // Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0. + if (denominator <= high) { + Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW)); + } + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [high low]. + uint256 remainder; + assembly ("memory-safe") { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number. + 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 ("memory-safe") { + // Divide denominator by twos. + denominator := div(denominator, 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 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 + // four bits. That is, denominator * inv ≡ 1 mod 2⁴. + uint256 inverse = (3 * denominator) ^ 2; + + // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also + // works in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2⁸ + inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶ + inverse *= 2 - denominator * inverse; // inverse mod 2³² + inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴ + inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸ + inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶ + + // 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 high + // is no longer required. + result = low * inverse; + return result; + } + } + + /** + * @dev Calculates x * y / denominator with full precision, following the selected rounding direction. + */ + function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { + 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. + * + * If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0. + * If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible. + * + * If the input value is not inversible, 0 is returned. + * + * NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the + * inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}. + */ + function invMod(uint256 a, uint256 n) internal pure returns (uint256) { + unchecked { + if (n == 0) return 0; + + // The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version) + // Used to compute integers x and y such that: ax + ny = gcd(a, n). + // When the gcd is 1, then the inverse of a modulo n exists and it's x. + // ax + ny = 1 + // ax = 1 + (-y)n + // ax ≡ 1 (mod n) # x is the inverse of a modulo n + + // If the remainder is 0 the gcd is n right away. + uint256 remainder = a % n; + uint256 gcd = n; + + // Therefore the initial coefficients are: + // ax + ny = gcd(a, n) = n + // 0a + 1n = n + int256 x = 0; + int256 y = 1; + + while (remainder != 0) { + uint256 quotient = gcd / remainder; + + (gcd, remainder) = ( + // The old remainder is the next gcd to try. + remainder, + // Compute the next remainder. + // Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd + // where gcd is at most n (capped to type(uint256).max) + gcd - remainder * quotient + ); + + (x, y) = ( + // Increment the coefficient of a. + y, + // Decrement the coefficient of n. + // Can overflow, but the result is casted to uint256 so that the + // next value of y is "wrapped around" to a value between 0 and n - 1. + x - y * int256(quotient) + ); + } + + if (gcd != 1) return 0; // No inverse exists. + return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative. + } + } + + /** + * @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`. + * + * From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is + * prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that + * `a**(p-2)` is the modular multiplicative inverse of a in Fp. + * + * NOTE: this function does NOT check that `p` is a prime greater than `2`. + */ + function invModPrime(uint256 a, uint256 p) internal view returns (uint256) { + unchecked { + return Math.modExp(a, p - 2, p); + } + } + + /** + * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m) + * + * Requirements: + * - modulus can't be zero + * - underlying staticcall to precompile must succeed + * + * IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make + * sure the chain you're using it on supports the precompiled contract for modular exponentiation + * at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, + * the underlying function will succeed given the lack of a revert, but the result may be incorrectly + * interpreted as 0. + */ + function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) { + (bool success, uint256 result) = tryModExp(b, e, m); + if (!success) { + Panic.panic(Panic.DIVISION_BY_ZERO); + } + return result; + } + + /** + * @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m). + * It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying + * to operate modulo 0 or if the underlying precompile reverted. + * + * IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain + * you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in + * https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack + * of a revert, but the result may be incorrectly interpreted as 0. + */ + function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) { + if (m == 0) return (false, 0); + assembly ("memory-safe") { + let ptr := mload(0x40) + // | Offset | Content | Content (Hex) | + // |-----------|------------|--------------------------------------------------------------------| + // | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 | + // | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 | + // | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 | + // | 0x60:0x7f | value of b | 0x<.............................................................b> | + // | 0x80:0x9f | value of e | 0x<.............................................................e> | + // | 0xa0:0xbf | value of m | 0x<.............................................................m> | + mstore(ptr, 0x20) + mstore(add(ptr, 0x20), 0x20) + mstore(add(ptr, 0x40), 0x20) + mstore(add(ptr, 0x60), b) + mstore(add(ptr, 0x80), e) + mstore(add(ptr, 0xa0), m) + + // Given the result < m, it's guaranteed to fit in 32 bytes, + // so we can use the memory scratch space located at offset 0. + success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20) + result := mload(0x00) + } + } + + /** + * @dev Variant of {modExp} that supports inputs of arbitrary length. + */ + function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) { + (bool success, bytes memory result) = tryModExp(b, e, m); + if (!success) { + Panic.panic(Panic.DIVISION_BY_ZERO); + } + return result; + } + + /** + * @dev Variant of {tryModExp} that supports inputs of arbitrary length. + */ + function tryModExp( + bytes memory b, + bytes memory e, + bytes memory m + ) internal view returns (bool success, bytes memory result) { + if (_zeroBytes(m)) return (false, new bytes(0)); + + uint256 mLen = m.length; + + // Encode call args in result and move the free memory pointer + result = abi.encodePacked(b.length, e.length, mLen, b, e, m); + + assembly ("memory-safe") { + let dataPtr := add(result, 0x20) + // Write result on top of args to avoid allocating extra memory. + success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen) + // Overwrite the length. + // result.length > returndatasize() is guaranteed because returndatasize() == m.length + mstore(result, mLen) + // Set the memory pointer after the returned data. + mstore(0x40, add(dataPtr, mLen)) + } + } + + /** + * @dev Returns whether the provided byte array is zero. + */ + function _zeroBytes(bytes memory byteArray) private pure returns (bool) { + for (uint256 i = 0; i < byteArray.length; ++i) { + if (byteArray[i] != 0) { + return false; + } + } + return true; + } + + /** + * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded + * towards zero. + * + * This method is based on Newton's method for computing square roots; the algorithm is restricted to only + * using integer operations. + */ + function sqrt(uint256 a) internal pure returns (uint256) { + unchecked { + // Take care of easy edge cases when a == 0 or a == 1 + if (a <= 1) { + return a; + } + + // In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a + // sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between + // the current value as `ε_n = | x_n - sqrt(a) |`. + // + // For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root + // of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is + // bigger than any uint256. + // + // By noticing that + // `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)` + // we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar + // to the msb function. + uint256 aa = a; + uint256 xn = 1; + + if (aa >= (1 << 128)) { + aa >>= 128; + xn <<= 64; + } + if (aa >= (1 << 64)) { + aa >>= 64; + xn <<= 32; + } + if (aa >= (1 << 32)) { + aa >>= 32; + xn <<= 16; + } + if (aa >= (1 << 16)) { + aa >>= 16; + xn <<= 8; + } + if (aa >= (1 << 8)) { + aa >>= 8; + xn <<= 4; + } + if (aa >= (1 << 4)) { + aa >>= 4; + xn <<= 2; + } + if (aa >= (1 << 2)) { + xn <<= 1; + } + + // We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1). + // + // We can refine our estimation by noticing that the middle of that interval minimizes the error. + // If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2). + // This is going to be our x_0 (and ε_0) + xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2) + + // From here, Newton's method give us: + // x_{n+1} = (x_n + a / x_n) / 2 + // + // One should note that: + // x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a + // = ((x_n² + a) / (2 * x_n))² - a + // = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a + // = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²) + // = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²) + // = (x_n² - a)² / (2 * x_n)² + // = ((x_n² - a) / (2 * x_n))² + // ≥ 0 + // Which proves that for all n ≥ 1, sqrt(a) ≤ x_n + // + // This gives us the proof of quadratic convergence of the sequence: + // ε_{n+1} = | x_{n+1} - sqrt(a) | + // = | (x_n + a / x_n) / 2 - sqrt(a) | + // = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) | + // = | (x_n - sqrt(a))² / (2 * x_n) | + // = | ε_n² / (2 * x_n) | + // = ε_n² / | (2 * x_n) | + // + // For the first iteration, we have a special case where x_0 is known: + // ε_1 = ε_0² / | (2 * x_0) | + // ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2))) + // ≤ 2**(2*e-4) / (3 * 2**(e-1)) + // ≤ 2**(e-3) / 3 + // ≤ 2**(e-3-log2(3)) + // ≤ 2**(e-4.5) + // + // For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n: + // ε_{n+1} = ε_n² / | (2 * x_n) | + // ≤ (2**(e-k))² / (2 * 2**(e-1)) + // ≤ 2**(2*e-2*k) / 2**e + // ≤ 2**(e-2*k) + xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above + xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5 + xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9 + xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18 + xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36 + xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72 + + // Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision + // ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either + // sqrt(a) or sqrt(a) + 1. + return xn - SafeCast.toUint(xn > a / xn); + } + } + + /** + * @dev Calculates sqrt(a), following the selected rounding direction. + */ + function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = sqrt(a); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a); + } + } + + /** + * @dev Return the log in base 2 of a positive value rounded towards zero. + * Returns 0 if given 0. + */ + 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; + + // 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)) + } + } + + /** + * @dev Return the log in base 2, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log2(value); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value); + } + } + + /** + * @dev Return the log in base 10 of a positive value rounded towards zero. + * Returns 0 if given 0. + */ + function log10(uint256 value) internal pure returns (uint256) { + uint256 result = 0; + unchecked { + if (value >= 10 ** 64) { + value /= 10 ** 64; + result += 64; + } + if (value >= 10 ** 32) { + value /= 10 ** 32; + result += 32; + } + if (value >= 10 ** 16) { + value /= 10 ** 16; + result += 16; + } + if (value >= 10 ** 8) { + value /= 10 ** 8; + result += 8; + } + if (value >= 10 ** 4) { + value /= 10 ** 4; + result += 4; + } + if (value >= 10 ** 2) { + value /= 10 ** 2; + result += 2; + } + if (value >= 10 ** 1) { + result += 1; + } + } + return result; + } + + /** + * @dev Return the log in base 10, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log10(value); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value); + } + } + + /** + * @dev Return the log in base 256 of a positive value rounded towards zero. + * Returns 0 if given 0. + * + * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. + */ + 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); + } + + /** + * @dev Return the log in base 256, following the selected rounding direction, of a positive value. + * Returns 0 if given 0. + */ + function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { + unchecked { + uint256 result = log256(value); + return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value); + } + } + + /** + * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. + */ + function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { + return uint8(rounding) % 2 == 1; + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/math/SignedMath.sol + + +// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SignedMath.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Standard signed math utilities missing in the Solidity language. + */ +library SignedMath { + /** + * @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant. + * + * IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone. + * However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute + * one branch when needed, making this function more expensive. + */ + function ternary(bool condition, int256 a, int256 b) internal pure returns (int256) { + unchecked { + // branchless ternary works because: + // b ^ (a ^ b) == a + // b ^ 0 == b + return b ^ ((a ^ b) * int256(SafeCast.toUint(condition))); + } + } + + /** + * @dev Returns the largest of two signed numbers. + */ + function max(int256 a, int256 b) internal pure returns (int256) { + return ternary(a > b, a, b); + } + + /** + * @dev Returns the smallest of two signed numbers. + */ + function min(int256 a, int256 b) internal pure returns (int256) { + return ternary(a < b, a, b); + } + + /** + * @dev Returns the average of two signed numbers without overflow. + * The result is rounded towards zero. + */ + function average(int256 a, int256 b) internal pure returns (int256) { + // Formula from the book "Hacker's Delight" + int256 x = (a & b) + ((a ^ b) >> 1); + return x + (int256(uint256(x) >> 255) & (a ^ b)); + } + + /** + * @dev Returns the absolute unsigned value of a signed value. + */ + function abs(int256 n) internal pure returns (uint256) { + unchecked { + // Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson. + // Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift, + // taking advantage of the most significant (or "sign" bit) in two's complement representation. + // This opcode adds new most significant bits set to the value of the previous most significant bit. As a result, + // the mask will either be `bytes32(0)` (if n is positive) or `~bytes32(0)` (if n is negative). + int256 mask = n >> 255; + + // A `bytes32(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it. + return uint256((n + mask) ^ mask); + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/Strings.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (utils/Strings.sol) + +pragma solidity ^0.8.20; + + + + +/** + * @dev String operations. + */ +library Strings { + using SafeCast for *; + + 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`. + */ + error StringsInsufficientHexLength(uint256 value, uint256 length); + + /** + * @dev The string being parsed contains characters that are not in scope of the given base. + */ + error StringsInvalidChar(); + + /** + * @dev The string being parsed is not a properly formatted address. + */ + error StringsInvalidAddressFormat(); + + /** + * @dev Converts a `uint256` to its ASCII `string` decimal representation. + */ + function toString(uint256 value) internal pure returns (string memory) { + unchecked { + uint256 length = Math.log10(value) + 1; + string memory buffer = new string(length); + uint256 ptr; + assembly ("memory-safe") { + ptr := add(add(buffer, 0x20), length) + } + while (true) { + ptr--; + assembly ("memory-safe") { + mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) + } + value /= 10; + if (value == 0) break; + } + return buffer; + } + } + + /** + * @dev Converts a `int256` to its ASCII `string` decimal representation. + */ + function toStringSigned(int256 value) internal pure returns (string memory) { + return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. + */ + function toHexString(uint256 value) internal pure returns (string memory) { + unchecked { + return toHexString(value, Math.log256(value) + 1); + } + } + + /** + * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. + */ + function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { + uint256 localValue = value; + bytes memory buffer = new bytes(2 * length + 2); + buffer[0] = "0"; + buffer[1] = "x"; + for (uint256 i = 2 * length + 1; i > 1; --i) { + buffer[i] = HEX_DIGITS[localValue & 0xf]; + localValue >>= 4; + } + if (localValue != 0) { + revert StringsInsufficientHexLength(value, length); + } + return string(buffer); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal + * representation. + */ + function toHexString(address addr) internal pure returns (string memory) { + return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); + } + + /** + * @dev Converts an `address` with fixed length of 20 bytes to its checksummed ASCII `string` hexadecimal + * representation, according to EIP-55. + */ + function toChecksumHexString(address addr) internal pure returns (string memory) { + bytes memory buffer = bytes(toHexString(addr)); + + // hash the hex part of buffer (skip length + 2 bytes, length 40) + uint256 hashValue; + assembly ("memory-safe") { + hashValue := shr(96, keccak256(add(buffer, 0x22), 40)) + } + + for (uint256 i = 41; i > 1; --i) { + // possible values for buffer[i] are 48 (0) to 57 (9) and 97 (a) to 102 (f) + if (hashValue & 0xf > 7 && uint8(buffer[i]) > 96) { + // case shift by xoring with 0x20 + buffer[i] ^= 0x20; + } + hashValue >>= 4; + } + return string(buffer); + } + + /** + * @dev Returns true if the two strings are equal. + */ + function equal(string memory a, string memory b) internal pure returns (bool) { + return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); + } + + /** + * @dev Parse a decimal string and returns the value as a `uint256`. + * + * Requirements: + * - The string must be formatted as `[0-9]*` + * - The result must fit into an `uint256` type + */ + function parseUint(string memory input) internal pure returns (uint256) { + return parseUint(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseUint-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `[0-9]*` + * - The result must fit into an `uint256` type + */ + function parseUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) { + (bool success, uint256 value) = tryParseUint(input, begin, end); + if (!success) revert StringsInvalidChar(); + return value; + } + + /** + * @dev Variant of {parseUint-string} that returns false if the parsing fails because of an invalid character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseUint(string memory input) internal pure returns (bool success, uint256 value) { + return _tryParseUintUncheckedBounds(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseUint-string-uint256-uint256} that returns false if the parsing fails because of an invalid + * character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseUint( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, uint256 value) { + if (end > bytes(input).length || begin > end) return (false, 0); + return _tryParseUintUncheckedBounds(input, begin, end); + } + + /** + * @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( + string memory input, + uint256 begin, + uint256 end + ) private pure returns (bool success, uint256 value) { + bytes memory buffer = bytes(input); + + uint256 result = 0; + for (uint256 i = begin; i < end; ++i) { + uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i))); + if (chr > 9) return (false, 0); + result *= 10; + result += chr; + } + return (true, result); + } + + /** + * @dev Parse a decimal string and returns the value as a `int256`. + * + * Requirements: + * - The string must be formatted as `[-+]?[0-9]*` + * - The result must fit in an `int256` type. + */ + function parseInt(string memory input) internal pure returns (int256) { + return parseInt(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseInt-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `[-+]?[0-9]*` + * - The result must fit in an `int256` type. + */ + function parseInt(string memory input, uint256 begin, uint256 end) internal pure returns (int256) { + (bool success, int256 value) = tryParseInt(input, begin, end); + if (!success) revert StringsInvalidChar(); + return value; + } + + /** + * @dev Variant of {parseInt-string} that returns false if the parsing fails because of an invalid character or if + * the result does not fit in a `int256`. + * + * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`. + */ + function tryParseInt(string memory input) internal pure returns (bool success, int256 value) { + return _tryParseIntUncheckedBounds(input, 0, bytes(input).length); + } + + uint256 private constant ABS_MIN_INT256 = 2 ** 255; + + /** + * @dev Variant of {parseInt-string-uint256-uint256} that returns false if the parsing fails because of an invalid + * character or if the result does not fit in a `int256`. + * + * NOTE: This function will revert if the absolute value of the result does not fit in a `uint256`. + */ + function tryParseInt( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, int256 value) { + if (end > bytes(input).length || begin > end) return (false, 0); + return _tryParseIntUncheckedBounds(input, begin, end); + } + + /** + * @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( + string memory input, + uint256 begin, + uint256 end + ) private pure returns (bool success, int256 value) { + bytes memory buffer = bytes(input); + + // Check presence of a negative sign. + bytes1 sign = begin == end ? bytes1(0) : bytes1(_unsafeReadBytesOffset(buffer, begin)); // don't do out-of-bound (possibly unsafe) read if sub-string is empty + bool positiveSign = sign == bytes1("+"); + bool negativeSign = sign == bytes1("-"); + uint256 offset = (positiveSign || negativeSign).toUint(); + + (bool absSuccess, uint256 absValue) = tryParseUint(input, begin + offset, end); + + if (absSuccess && absValue < ABS_MIN_INT256) { + return (true, negativeSign ? -int256(absValue) : int256(absValue)); + } else if (absSuccess && negativeSign && absValue == ABS_MIN_INT256) { + return (true, type(int256).min); + } else return (false, 0); + } + + /** + * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as a `uint256`. + * + * Requirements: + * - The string must be formatted as `(0x)?[0-9a-fA-F]*` + * - The result must fit in an `uint256` type. + */ + function parseHexUint(string memory input) internal pure returns (uint256) { + return parseHexUint(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseHexUint-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `(0x)?[0-9a-fA-F]*` + * - The result must fit in an `uint256` type. + */ + function parseHexUint(string memory input, uint256 begin, uint256 end) internal pure returns (uint256) { + (bool success, uint256 value) = tryParseHexUint(input, begin, end); + if (!success) revert StringsInvalidChar(); + return value; + } + + /** + * @dev Variant of {parseHexUint-string} that returns false if the parsing fails because of an invalid character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseHexUint(string memory input) internal pure returns (bool success, uint256 value) { + return _tryParseHexUintUncheckedBounds(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseHexUint-string-uint256-uint256} that returns false if the parsing fails because of an + * invalid character. + * + * NOTE: This function will revert if the result does not fit in a `uint256`. + */ + function tryParseHexUint( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, uint256 value) { + if (end > bytes(input).length || begin > end) return (false, 0); + return _tryParseHexUintUncheckedBounds(input, begin, end); + } + + /** + * @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( + string memory input, + uint256 begin, + uint256 end + ) private pure returns (bool success, uint256 value) { + bytes memory buffer = bytes(input); + + // skip 0x prefix if present + bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(buffer, begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty + uint256 offset = hasPrefix.toUint() * 2; + + uint256 result = 0; + for (uint256 i = begin + offset; i < end; ++i) { + uint8 chr = _tryParseChr(bytes1(_unsafeReadBytesOffset(buffer, i))); + if (chr > 15) return (false, 0); + result *= 16; + unchecked { + // Multiplying by 16 is equivalent to a shift of 4 bits (with additional overflow check). + // This guarantees that adding a value < 16 will not cause an overflow, hence the unchecked. + result += chr; + } + } + return (true, result); + } + + /** + * @dev Parse a hexadecimal string (with or without "0x" prefix), and returns the value as an `address`. + * + * Requirements: + * - The string must be formatted as `(0x)?[0-9a-fA-F]{40}` + */ + function parseAddress(string memory input) internal pure returns (address) { + return parseAddress(input, 0, bytes(input).length); + } + + /** + * @dev Variant of {parseAddress-string} that parses a substring of `input` located between position `begin` (included) and + * `end` (excluded). + * + * Requirements: + * - The substring must be formatted as `(0x)?[0-9a-fA-F]{40}` + */ + function parseAddress(string memory input, uint256 begin, uint256 end) internal pure returns (address) { + (bool success, address value) = tryParseAddress(input, begin, end); + if (!success) revert StringsInvalidAddressFormat(); + return value; + } + + /** + * @dev Variant of {parseAddress-string} that returns false if the parsing fails because the input is not a properly + * 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); + } + + /** + * @dev Variant of {parseAddress-string-uint256-uint256} that returns false if the parsing fails because input is not a properly + * formatted address. See {parseAddress-string-uint256-uint256} requirements. + */ + function tryParseAddress( + string memory input, + uint256 begin, + uint256 end + ) internal pure returns (bool success, address value) { + if (end > bytes(input).length || begin > end) return (false, address(0)); + + bool hasPrefix = (end > begin + 1) && bytes2(_unsafeReadBytesOffset(bytes(input), begin)) == bytes2("0x"); // don't do out-of-bound (possibly unsafe) read if sub-string is empty + uint256 expectedLength = 40 + hasPrefix.toUint() * 2; + + // check that input is the correct length + if (end - begin == expectedLength) { + // length guarantees that this does not overflow, and value is at most type(uint160).max + (bool s, uint256 v) = _tryParseHexUintUncheckedBounds(input, begin, end); + return (s, address(uint160(v))); + } else { + return (false, address(0)); + } + } + + function _tryParseChr(bytes1 chr) private pure returns (uint8) { + uint8 value = uint8(chr); + + // Try to parse `chr`: + // - Case 1: [0-9] + // - Case 2: [a-f] + // - Case 3: [A-F] + // - otherwise not supported + unchecked { + if (value > 47 && value < 58) value -= 48; + else if (value > 96 && value < 103) value -= 87; + else if (value > 64 && value < 71) value -= 55; + else return type(uint8).max; + } + + 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. + * + * NOTE: making this function internal would mean it could be used with memory unsafe offset, and marking the + * assembly block as such would prevent some optimizations. + */ + 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(add(buffer, 0x20), offset)) + } + } +} + +// File: @openzeppelin/contracts@5.4.0/utils/introspection/ERC165.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Implementation of the {IERC165} interface. + * + * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check + * for the additional interface id that will be supported. For example: + * + * ```solidity + * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); + * } + * ``` + */ +abstract contract ERC165 is IERC165 { + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { + return interfaceId == type(IERC165).interfaceId; + } +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/ERC721.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/ERC721.sol) + +pragma solidity ^0.8.20; + + + + + + + + +/** + * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC-721] Non-Fungible Token Standard, including + * the Metadata extension, but not including the Enumerable extension, which is available separately as + * {ERC721Enumerable}. + */ +abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Errors { + using Strings for uint256; + + // Token name + string private _name; + + // Token symbol + string private _symbol; + + mapping(uint256 tokenId => address) private _owners; + + mapping(address owner => uint256) private _balances; + + mapping(uint256 tokenId => address) private _tokenApprovals; + + mapping(address owner => mapping(address operator => bool)) private _operatorApprovals; + + /** + * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + } + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IERC721).interfaceId || + interfaceId == type(IERC721Metadata).interfaceId || + super.supportsInterface(interfaceId); + } + + /// @inheritdoc IERC721 + function balanceOf(address owner) public view virtual returns (uint256) { + if (owner == address(0)) { + revert ERC721InvalidOwner(address(0)); + } + return _balances[owner]; + } + + /// @inheritdoc IERC721 + function ownerOf(uint256 tokenId) public view virtual returns (address) { + return _requireOwned(tokenId); + } + + /// @inheritdoc IERC721Metadata + function name() public view virtual returns (string memory) { + return _name; + } + + /// @inheritdoc IERC721Metadata + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /// @inheritdoc IERC721Metadata + function tokenURI(uint256 tokenId) public view virtual returns (string memory) { + _requireOwned(tokenId); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : ""; + } + + /** + * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each + * token will be the concatenation of the `baseURI` and the `tokenId`. Empty + * by default, can be overridden in child contracts. + */ + function _baseURI() internal view virtual returns (string memory) { + return ""; + } + + /// @inheritdoc IERC721 + function approve(address to, uint256 tokenId) public virtual { + _approve(to, tokenId, _msgSender()); + } + + /// @inheritdoc IERC721 + function getApproved(uint256 tokenId) public view virtual returns (address) { + _requireOwned(tokenId); + + return _getApproved(tokenId); + } + + /// @inheritdoc IERC721 + function setApprovalForAll(address operator, bool approved) public virtual { + _setApprovalForAll(_msgSender(), operator, approved); + } + + /// @inheritdoc IERC721 + function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { + return _operatorApprovals[owner][operator]; + } + + /// @inheritdoc IERC721 + function transferFrom(address from, address to, uint256 tokenId) public virtual { + if (to == address(0)) { + revert ERC721InvalidReceiver(address(0)); + } + // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists + // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. + address previousOwner = _update(to, tokenId, _msgSender()); + if (previousOwner != from) { + revert ERC721IncorrectOwner(from, tokenId, previousOwner); + } + } + + /// @inheritdoc IERC721 + function safeTransferFrom(address from, address to, uint256 tokenId) public { + safeTransferFrom(from, to, tokenId, ""); + } + + /// @inheritdoc IERC721 + function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual { + transferFrom(from, to, tokenId); + ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data); + } + + /** + * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist + * + * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the + * core ERC-721 logic MUST be matched with the use of {_increaseBalance} to keep balances + * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by + * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`. + */ + function _ownerOf(uint256 tokenId) internal view virtual returns (address) { + return _owners[tokenId]; + } + + /** + * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted. + */ + function _getApproved(uint256 tokenId) internal view virtual returns (address) { + return _tokenApprovals[tokenId]; + } + + /** + * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in + * particular (ignoring whether it is owned by `owner`). + * + * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this + * assumption. + */ + function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) { + return + spender != address(0) && + (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender); + } + + /** + * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner. + * Reverts if: + * - `spender` does not have approval from `owner` for `tokenId`. + * - `spender` does not have approval to manage all of `owner`'s assets. + * + * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this + * assumption. + */ + function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual { + if (!_isAuthorized(owner, spender, tokenId)) { + if (owner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } else { + revert ERC721InsufficientApproval(spender, tokenId); + } + } + } + + /** + * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override. + * + * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that + * a uint256 would ever overflow from increments when these increments are bounded to uint128 values. + * + * WARNING: Increasing an account's balance using this function tends to be paired with an override of the + * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership + * remain consistent with one another. + */ + function _increaseBalance(address account, uint128 value) internal virtual { + unchecked { + _balances[account] += value; + } + } + + /** + * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner + * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update. + * + * The `auth` argument is optional. If the value passed is non 0, then this function will check that + * `auth` is either the owner of the token, or approved to operate on the token (by the owner). + * + * Emits a {Transfer} event. + * + * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}. + */ + function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) { + address from = _ownerOf(tokenId); + + // Perform (optional) operator check + if (auth != address(0)) { + _checkAuthorized(from, auth, tokenId); + } + + // Execute the update + if (from != address(0)) { + // Clear approval. No need to re-authorize or emit the Approval event + _approve(address(0), tokenId, address(0), false); + + unchecked { + _balances[from] -= 1; + } + } + + if (to != address(0)) { + unchecked { + _balances[to] += 1; + } + } + + _owners[tokenId] = to; + + emit Transfer(from, to, tokenId); + + return from; + } + + /** + * @dev Mints `tokenId` and transfers it to `to`. + * + * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible + * + * Requirements: + * + * - `tokenId` must not exist. + * - `to` cannot be the zero address. + * + * Emits a {Transfer} event. + */ + function _mint(address to, uint256 tokenId) internal { + if (to == address(0)) { + revert ERC721InvalidReceiver(address(0)); + } + address previousOwner = _update(to, tokenId, address(0)); + if (previousOwner != address(0)) { + revert ERC721InvalidSender(address(0)); + } + } + + /** + * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance. + * + * Requirements: + * + * - `tokenId` must not exist. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeMint(address to, uint256 tokenId) internal { + _safeMint(to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { + _mint(to, tokenId); + ERC721Utils.checkOnERC721Received(_msgSender(), address(0), to, tokenId, data); + } + + /** + * @dev Destroys `tokenId`. + * The approval is cleared when the token is burned. + * This is an internal function that does not check if the sender is authorized to operate on the token. + * + * Requirements: + * + * - `tokenId` must exist. + * + * Emits a {Transfer} event. + */ + function _burn(uint256 tokenId) internal { + address previousOwner = _update(address(0), tokenId, address(0)); + if (previousOwner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } + } + + /** + * @dev Transfers `tokenId` from `from` to `to`. + * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. + * + * Requirements: + * + * - `to` cannot be the zero address. + * - `tokenId` token must be owned by `from`. + * + * Emits a {Transfer} event. + */ + function _transfer(address from, address to, uint256 tokenId) internal { + if (to == address(0)) { + revert ERC721InvalidReceiver(address(0)); + } + address previousOwner = _update(to, tokenId, address(0)); + if (previousOwner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } else if (previousOwner != from) { + revert ERC721IncorrectOwner(from, tokenId, previousOwner); + } + } + + /** + * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients + * are aware of the ERC-721 standard to prevent tokens from being forever locked. + * + * `data` is additional data, it has no specified format and it is sent in call to `to`. + * + * This internal function is like {safeTransferFrom} in the sense that it invokes + * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g. + * implement alternative mechanisms to perform token transfer, such as signature-based. + * + * Requirements: + * + * - `tokenId` token must exist and be owned by `from`. + * - `to` cannot be the zero address. + * - `from` cannot be the zero address. + * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. + * + * Emits a {Transfer} event. + */ + function _safeTransfer(address from, address to, uint256 tokenId) internal { + _safeTransfer(from, to, tokenId, ""); + } + + /** + * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is + * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. + */ + function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual { + _transfer(from, to, tokenId); + ERC721Utils.checkOnERC721Received(_msgSender(), from, to, tokenId, data); + } + + /** + * @dev Approve `to` to operate on `tokenId` + * + * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is + * either the owner of the token, or approved to operate on all tokens held by this owner. + * + * Emits an {Approval} event. + * + * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. + */ + function _approve(address to, uint256 tokenId, address auth) internal { + _approve(to, tokenId, auth, true); + } + + /** + * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not + * emitted in the context of transfers. + */ + function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual { + // Avoid reading the owner unless necessary + if (emitEvent || auth != address(0)) { + address owner = _requireOwned(tokenId); + + // We do not use _isAuthorized because single-token approvals should not be able to call approve + if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) { + revert ERC721InvalidApprover(auth); + } + + if (emitEvent) { + emit Approval(owner, to, tokenId); + } + } + + _tokenApprovals[tokenId] = to; + } + + /** + * @dev Approve `operator` to operate on all of `owner` tokens + * + * Requirements: + * - operator can't be the address zero. + * + * Emits an {ApprovalForAll} event. + */ + function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { + if (operator == address(0)) { + revert ERC721InvalidOperator(operator); + } + _operatorApprovals[owner][operator] = approved; + emit ApprovalForAll(owner, operator, approved); + } + + /** + * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned). + * Returns the owner. + * + * Overrides to ownership logic should be done to {_ownerOf}. + */ + function _requireOwned(uint256 tokenId) internal view returns (address) { + address owner = _ownerOf(tokenId); + if (owner == address(0)) { + revert ERC721NonexistentToken(tokenId); + } + return owner; + } +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/extensions/IERC721Enumerable.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/IERC721Enumerable.sol) + +pragma solidity >=0.6.2; + + +/** + * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension + * @dev See https://eips.ethereum.org/EIPS/eip-721 + */ +interface IERC721Enumerable is IERC721 { + /** + * @dev Returns the total amount of tokens stored by the contract. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns a token ID owned by `owner` at a given `index` of its token list. + * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. + */ + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + + /** + * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. + * Use along with {totalSupply} to enumerate all tokens. + */ + function tokenByIndex(uint256 index) external view returns (uint256); +} + +// File: @openzeppelin/contracts@5.4.0/token/ERC721/extensions/ERC721Enumerable.sol + + +// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC721/extensions/ERC721Enumerable.sol) + +pragma solidity ^0.8.20; + + + + +/** + * @dev This implements an optional extension of {ERC721} defined in the ERC that adds enumerability + * of all the token ids in the contract as well as all token ids owned by each account. + * + * CAUTION: {ERC721} extensions that implement custom `balanceOf` logic, such as {ERC721Consecutive}, + * interfere with enumerability and should not be used together with {ERC721Enumerable}. + */ +abstract contract ERC721Enumerable is ERC721, IERC721Enumerable { + mapping(address owner => mapping(uint256 index => uint256)) private _ownedTokens; + mapping(uint256 tokenId => uint256) private _ownedTokensIndex; + + uint256[] private _allTokens; + mapping(uint256 tokenId => uint256) private _allTokensIndex; + + /** + * @dev An `owner`'s token query was out of bounds for `index`. + * + * NOTE: The owner being `address(0)` indicates a global out of bounds index. + */ + error ERC721OutOfBoundsIndex(address owner, uint256 index); + + /** + * @dev Batch mint is not allowed. + */ + error ERC721EnumerableForbiddenBatchMint(); + + /// @inheritdoc IERC165 + function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC721) returns (bool) { + return interfaceId == type(IERC721Enumerable).interfaceId || super.supportsInterface(interfaceId); + } + + /// @inheritdoc IERC721Enumerable + function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual returns (uint256) { + if (index >= balanceOf(owner)) { + revert ERC721OutOfBoundsIndex(owner, index); + } + return _ownedTokens[owner][index]; + } + + /// @inheritdoc IERC721Enumerable + function totalSupply() public view virtual returns (uint256) { + return _allTokens.length; + } + + /// @inheritdoc IERC721Enumerable + function tokenByIndex(uint256 index) public view virtual returns (uint256) { + if (index >= totalSupply()) { + revert ERC721OutOfBoundsIndex(address(0), index); + } + return _allTokens[index]; + } + + /// @inheritdoc ERC721 + function _update(address to, uint256 tokenId, address auth) internal virtual override returns (address) { + address previousOwner = super._update(to, tokenId, auth); + + if (previousOwner == address(0)) { + _addTokenToAllTokensEnumeration(tokenId); + } else if (previousOwner != to) { + _removeTokenFromOwnerEnumeration(previousOwner, tokenId); + } + if (to == address(0)) { + _removeTokenFromAllTokensEnumeration(tokenId); + } else if (previousOwner != to) { + _addTokenToOwnerEnumeration(to, tokenId); + } + + return previousOwner; + } + + /** + * @dev Private function to add a token to this extension's ownership-tracking data structures. + * @param to address representing the new owner of the given token ID + * @param tokenId uint256 ID of the token to be added to the tokens list of the given address + */ + function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private { + uint256 length = balanceOf(to) - 1; + _ownedTokens[to][length] = tokenId; + _ownedTokensIndex[tokenId] = length; + } + + /** + * @dev Private function to add a token to this extension's token tracking data structures. + * @param tokenId uint256 ID of the token to be added to the tokens list + */ + function _addTokenToAllTokensEnumeration(uint256 tokenId) private { + _allTokensIndex[tokenId] = _allTokens.length; + _allTokens.push(tokenId); + } + + /** + * @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that + * while the token is not assigned a new owner, the `_ownedTokensIndex` mapping is _not_ updated: this allows for + * gas optimizations e.g. when performing a transfer operation (avoiding double writes). + * This has O(1) time complexity, but alters the order of the _ownedTokens array. + * @param from address representing the previous owner of the given token ID + * @param tokenId uint256 ID of the token to be removed from the tokens list of the given address + */ + function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private { + // To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = balanceOf(from); + uint256 tokenIndex = _ownedTokensIndex[tokenId]; + + mapping(uint256 index => uint256) storage _ownedTokensByOwner = _ownedTokens[from]; + + // When the token to delete is the last token, the swap operation is unnecessary + if (tokenIndex != lastTokenIndex) { + uint256 lastTokenId = _ownedTokensByOwner[lastTokenIndex]; + + _ownedTokensByOwner[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + } + + // This also deletes the contents at the last position of the array + delete _ownedTokensIndex[tokenId]; + delete _ownedTokensByOwner[lastTokenIndex]; + } + + /** + * @dev Private function to remove a token from this extension's token tracking data structures. + * This has O(1) time complexity, but alters the order of the _allTokens array. + * @param tokenId uint256 ID of the token to be removed from the tokens list + */ + function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private { + // To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and + // then delete the last slot (swap and pop). + + uint256 lastTokenIndex = _allTokens.length - 1; + uint256 tokenIndex = _allTokensIndex[tokenId]; + + // When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so + // rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding + // an 'if' statement (like in _removeTokenFromOwnerEnumeration) + uint256 lastTokenId = _allTokens[lastTokenIndex]; + + _allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token + _allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index + + // This also deletes the contents at the last position of the array + delete _allTokensIndex[tokenId]; + _allTokens.pop(); + } + + /** + * See {ERC721-_increaseBalance}. We need that to account tokens that were minted in batch + */ + function _increaseBalance(address account, uint128 amount) internal virtual override { + if (amount > 0) { + revert ERC721EnumerableForbiddenBatchMint(); + } + super._increaseBalance(account, amount); + } +} + +// File: @openzeppelin/contracts@5.4.0/access/Ownable.sol + + +// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) + +pragma solidity ^0.8.20; + + +/** + * @dev Contract module which provides a basic access control mechanism, where + * there is an account (an owner) that can be granted exclusive access to + * specific functions. + * + * The initial owner is set to the address provided by the deployer. This can + * later be changed with {transferOwnership}. + * + * This module is used through inheritance. It will make available the modifier + * `onlyOwner`, which can be applied to your functions to restrict their use to + * the owner. + */ +abstract contract Ownable is Context { + address private _owner; + + /** + * @dev The caller account is not authorized to perform an operation. + */ + error OwnableUnauthorizedAccount(address account); + + /** + * @dev The owner is not a valid owner account. (eg. `address(0)`) + */ + error OwnableInvalidOwner(address owner); + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + /** + * @dev Initializes the contract setting the address provided by the deployer as the initial owner. + */ + constructor(address initialOwner) { + if (initialOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(initialOwner); + } + + /** + * @dev Throws if called by any account other than the owner. + */ + modifier onlyOwner() { + _checkOwner(); + _; + } + + /** + * @dev Returns the address of the current owner. + */ + function owner() public view virtual returns (address) { + return _owner; + } + + /** + * @dev Throws if the sender is not the owner. + */ + function _checkOwner() internal view virtual { + if (owner() != _msgSender()) { + revert OwnableUnauthorizedAccount(_msgSender()); + } + } + + /** + * @dev Leaves the contract without owner. It will not be possible to call + * `onlyOwner` functions. Can only be called by the current owner. + * + * NOTE: Renouncing ownership will leave the contract without an owner, + * thereby disabling any functionality that is only available to the owner. + */ + function renounceOwnership() public virtual onlyOwner { + _transferOwnership(address(0)); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Can only be called by the current owner. + */ + function transferOwnership(address newOwner) public virtual onlyOwner { + if (newOwner == address(0)) { + revert OwnableInvalidOwner(address(0)); + } + _transferOwnership(newOwner); + } + + /** + * @dev Transfers ownership of the contract to a new account (`newOwner`). + * Internal function without access restriction. + */ + function _transferOwnership(address newOwner) internal virtual { + address oldOwner = _owner; + _owner = newOwner; + emit OwnershipTransferred(oldOwner, newOwner); + } +} + +// File: contracts/NFTNumbered.sol + + +pragma solidity ^0.8.27; + + + + +/// @title NFTNumbered +/// @notice ERC721 contract with sequential variants for immutable metadata for minted tokens, allowing to change metadata for future tokens. +/// @dev Non-upgradeable. Minter address is settable by owner. Global sequential token ID. +contract NFTNumbered is ERC721Enumerable, Ownable { + address public minter; + bool public mintingLocked; + TokenInfo[] private tokenInfos; + uint public nextTokenId = 1; + + struct TokenInfo { + uint tokenId; + string tokenUri; + } + + constructor( + string memory _name, + string memory _symbol, + string memory _tokenUri + ) ERC721(_name, _symbol) Ownable(msg.sender) { + minter = msg.sender; + mintingLocked = false; + setNextTokenURI(_tokenUri); + } + + event MinterUpdated(address indexed newMinter); + event MintingLocked(); + event NextTokenURI(uint nextTokenId, string nextTokenUri); + + /// @notice Updates the minter address. + function setMinter(address newMinter) external onlyOwner whenNotLocked { + minter = newMinter; + emit MinterUpdated(newMinter); + } + + /// @notice Permanently disable minting and changes. + function lockMintingPermanently() external onlyOwner { + mintingLocked = true; + emit MintingLocked(); + } + + modifier whenNotLocked() { + require(!mintingLocked, "Contract permanently locked for changes and minting"); + _; + } + + /// @notice Sets tokenUri for future mints only. + function setNextTokenURI(string memory newTokenUri) public onlyOwner whenNotLocked { + uint len = tokenInfos.length; + TokenInfo memory newInfo = TokenInfo({tokenId: nextTokenId, tokenUri: newTokenUri}); + if (len == 0 || tokenInfos[len - 1].tokenId < nextTokenId) { // add + tokenInfos.push(newInfo); + } else { // replace + tokenInfos[len - 1] = newInfo; + } + emit NextTokenURI(nextTokenId, newTokenUri); + } + + /// @notice metadata URI of the token that will be minted next + function nextTokenURI() external view returns (string memory) { + return tokenInfos[tokenInfos.length - 1].tokenUri; + } + + /// @notice Mints a new token. + /// @param to The recipient of the token. + function mint(address to) external whenNotLocked { + require(msg.sender == owner() || msg.sender == minter, "Caller must be the owner or minter"); + require(to != address(0), "Cannot mint to zero address"); + uint tokenId = nextTokenId++; + _safeMint(to, tokenId); + } + + /// @notice Burn owned token. + function burn(uint tokenId) external { + _burn(tokenId); + } + + /// @notice prohibit approvals + // address to, uint256 tokenId + function approve(address, uint256) public virtual override (ERC721, IERC721) { + revert("Soulbound token: approvals prohibited"); + } + + /// @notice prohibit approvals + // address operator, bool approved + function setApprovalForAll(address, bool) public virtual override (ERC721, IERC721) { + revert("Soulbound token: approvals prohibited"); + } + + /// @notice Limits ownership to 1 token. + function _update(address to, uint tokenId, address auth) internal virtual override returns (address) { + require(to == address(0) || balanceOf(to) == 0, "Soulbound token: only 1 per address"); + require(to == address(0) || _ownerOf(tokenId) == address(0), "Soulbound token: transfers prohibited"); + return super._update(to, tokenId, auth); + } + + /// @notice Returns embedded JSON metadata URI. + /// @param id Token ID. + function tokenURI(uint id) public view virtual override returns (string memory) { + _requireOwned(id); + uint len = tokenInfos.length; + for (uint i = len; i > 0; ) { + unchecked { i--; } + if (tokenInfos[i].tokenId > id) continue; + return tokenInfos[i].tokenUri; + } + revert("Unknown token ID"); + } + + /// @notice Withdraw any accidental ETH. + function withdraw() external onlyOwner { + (bool success, ) = payable(owner()).call{value: address(this).balance}(""); + require(success, "Withdraw failed"); + } +} diff --git a/eth/nft/scripts/deploy_with_ethers.ts b/eth/nft/scripts/deploy_with_ethers.ts new file mode 100644 index 0000000000..63533fd6f5 --- /dev/null +++ b/eth/nft/scripts/deploy_with_ethers.ts @@ -0,0 +1,10 @@ +import { deploy } from './ethers-lib' + +(async () => { + try { + const result = await deploy('MyToken', []) + console.log(`address: ${result.address}`) + } catch (e) { + console.log(e.message) + } +})() \ No newline at end of file diff --git a/eth/nft/tests/MultiERC1155_test.sol b/eth/nft/tests/MultiERC1155_test.sol new file mode 100644 index 0000000000..f0152ad657 --- /dev/null +++ b/eth/nft/tests/MultiERC1155_test.sol @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import "remix_tests.sol"; + +import "../contracts/MultiERC1155.sol"; + +contract MultiERC1155Test { + MultiERC1155 public ct; + address public owner = address(this); + address public admin = address(0x1); + address public minter = address(0x2); + address public user = address(0x3); + address public recipient = address(0x4); + + MultiERC1155.TokenInfo defaultInfo = MultiERC1155.TokenInfo({ + tokenUri: "https://example.com/token.json", + totalSupply: 0, + enabled: true + }); + + function beforeAll() public { + ct = new MultiERC1155(); + MultiERC1155.TokenInfo memory newInfo = defaultInfo; + (bool success, ) = address(ct).call(abi.encodeWithSignature("addToken((string,bool,string,string,string,uint256))", newInfo)); + Assert.equal(success, true, "addToken success"); + } + + // Constructor Test + function testConstructor() public { + Assert.equal(ct.owner(), owner, "Owner should be deployer"); + Assert.equal(ct.admin(), owner, "Admin should be deployer"); + Assert.equal(ct.minter(), owner, "Minter should be deployer"); + Assert.equal(ct.mintingEnabled(), true, "Minting should be enabled"); + Assert.equal(ct.contractLocked(), false, "Contract should not be locked"); + uint[] memory ids = ct.getTokenIds(); + Assert.equal(ids.length, uint(1), "One token ID added"); + Assert.equal(ids[0], uint(1), "First token ID is 1"); + (, uint currentSupply, bool exists, bool locked) = ct.tokens(1); + Assert.equal(exists, true, "Token 1 exists"); + Assert.equal(locked, false, "Token 1 not locked"); + Assert.equal(currentSupply, uint(0), "Token 1 supply 0"); + } + + // setAdmin Test + function testSetAdmin() public { + ct.setAdmin(admin); + Assert.equal(ct.admin(), admin, "Admin updated"); + } + + function testSetAdminOnlyOwner() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("setAdmin(address)", admin)); + Assert.equal(success, true, "Set admin from owner succeeds"); // Since this is owner + + // To test revert, Remix plugin runs as this, so for non-owner, manual simulation not easy; note as TODO or skip + } + + // setMinter Test + function testSetMinter() public { + ct.setMinter(minter); + Assert.equal(ct.minter(), minter, "Minter updated"); + } + + function testSetMinterOnlyOwner() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("setMinter(address)", minter)); + Assert.equal(success, true, "Set minter from owner succeeds"); + } + + // toggleMinting Test + function testToggleMinting() public { + ct.toggleMinting(false); + Assert.equal(ct.mintingEnabled(), false, "Minting disabled"); + + ct.toggleMinting(true); + Assert.equal(ct.mintingEnabled(), true, "Minting enabled"); + } + + function testToggleMintingNoChange() public { + ct.toggleMinting(true); // Already true + Assert.equal(ct.mintingEnabled(), true, "No change if same"); + } + + function testToggleMintingOnlyOwner() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("toggleMinting(bool)", false)); + Assert.equal(success, true, "Toggle from owner succeeds"); + } + + // lockContract Test + function testLockContract() public { + ct.lockContract(); + Assert.equal(ct.contractLocked(), true, "Contract locked"); + Assert.equal(ct.mintingEnabled(), false, "Minting disabled"); + } + + function testLockContractOnlyOwner() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("lockContract()")); + Assert.equal(success, true, "Lock from owner succeeds"); + } + + // addToken Test + function testAddToken() public { + MultiERC1155.TokenInfo memory newInfo = MultiERC1155.TokenInfo({ + tokenUri: "https://example.com/new_token.json", + totalSupply: 100, + enabled: true + }); + + ct.addToken(newInfo); + uint[] memory ids = ct.getTokenIds(); + Assert.equal(ids.length, uint(2), "Two token IDs"); + Assert.equal(ids[1], uint(2), "Second token ID 2"); + (MultiERC1155.TokenInfo memory tokenInfo, uint currentSupply, bool exists, bool locked) = ct.tokens(2); + Assert.equal(exists, true, "Token 2 exists"); + Assert.equal(locked, false, "Token 2 not locked"); + Assert.equal(currentSupply, uint(0), "Token 2 supply 0"); + Assert.equal(tokenInfo.totalSupply, uint(100), "Token 2 totalSupply 100"); + // Validate info fields as needed + } + + function testAddTokenByAdmin() public { + ct.setAdmin(admin); + MultiERC1155.TokenInfo memory newInfo = defaultInfo; + (bool success, ) = address(ct).call(abi.encodeWithSignature("addToken((string,bool,string,string,string,uint256))", newInfo)); + Assert.equal(success, true, "Add by admin succeeds"); + uint[] memory ids = ct.getTokenIds(); + Assert.equal(ids.length, uint(2), "Two token IDs"); + } + + function testAddTokenRevertInvalidInfo() public { + MultiERC1155.TokenInfo memory invalidInfo = MultiERC1155.TokenInfo({ + tokenUri: "", + totalSupply: 0, + enabled: true + }); + (bool success, ) = address(ct).call(abi.encodeWithSignature("addToken((string,bool,string,string,string,uint256))", invalidInfo)); + Assert.equal(success, false, "Invalid info reverts"); + } + + function testAddTokenOnlyAdminOrOwner() public { + MultiERC1155.TokenInfo memory info = defaultInfo; + (bool success, ) = address(ct).call(abi.encodeWithSignature("addToken((string,bool,string,string,string,uint256))", info)); + Assert.equal(success, true, "Add from owner succeeds"); + } + + // removeToken Test + function testRemoveToken() public { + MultiERC1155.TokenInfo memory newInfo = defaultInfo; + ct.addToken(newInfo); + uint[] memory ids = ct.getTokenIds(); + Assert.equal(ids.length, uint(2), "Two token IDs"); + + ct.removeToken(2); + ids = ct.getTokenIds(); + Assert.equal(ids.length, uint(1), "One token ID after removal"); + (,,bool exists,) = ct.tokens(2); + Assert.equal(exists, false, "Token 2 removed"); + } + + function testRemoveTokenRevertNonExist() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("removeToken(uint256)", 99)); + Assert.equal(success, false, "Remove non-exist reverts"); + } + + function testRemoveTokenRevertHasSupply() public { + ct.mint(user, 1, 10, ""); + (bool success,) = address(ct).call(abi.encodeWithSignature("removeToken(uint256)", 1)); + Assert.equal(success, false, "Remove with supply reverts"); + } + + function testRemoveTokenRevertLast() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("removeToken(uint256)", 1)); + Assert.equal(success, false, "Remove last reverts"); + } + + function testRemoveTokenOnlyAdminOrOwner() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("removeToken(uint256)", 1)); + Assert.equal(success, false, "Remove last fails"); // But for non-owner, need simulation + } + + // updateToken Test + function testUpdateToken() public { + ct.updateToken(1, false, 100); + (MultiERC1155.TokenInfo memory tokenInfo,,,) = ct.tokens(1); + Assert.equal(tokenInfo.enabled, false, "Enabled updated"); + Assert.equal(tokenInfo.totalSupply, uint(100), "Total supply updated"); + } + + function testUpdateTokenNoChange() public { + (MultiERC1155.TokenInfo memory tokenInfo,,,) = ct.tokens(1); + ct.updateToken(1, tokenInfo.enabled, tokenInfo.totalSupply); + // No assert, as no change + } + + function testUpdateTokenRevertNonExist() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("updateToken(uint256,bool,uint256)", 99, true, 0)); + Assert.equal(success, false, "Update non-exist reverts"); + } + + function testUpdateTokenRevertLocked() public { + ct.mint(user, 1, 10, ""); + ct.lockToken(1); + (bool success, ) = address(ct).call(abi.encodeWithSignature("updateToken(uint256,bool,uint256)", 1, true, 0)); + Assert.equal(success, false, "Update locked reverts"); + } + + function testUpdateTokenRevertInvalidSupply() public { + ct.mint(user, 1, 10, ""); + (bool success, ) = address(ct).call(abi.encodeWithSignature("updateToken(uint256,bool,uint256)", 1, true, 5)); + Assert.equal(success, false, "Invalid supply reverts"); + } + + function testUpdateTokenOnlyAdminOrOwner() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("updateToken(uint256,bool,uint256)", 1, true, 0)); + Assert.equal(success, true, "Update from owner succeeds"); + } + + // lockToken Test + function testLockToken() public { + ct.mint(user, 1, 10, ""); + ct.lockToken(1); + (MultiERC1155.TokenInfo memory tokenInfo,,, bool locked) = ct.tokens(1); + Assert.equal(locked, true, "Token locked"); + Assert.equal(tokenInfo.enabled, false, "Enabled false"); + } + + function testLockTokenRevertNonExist() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("lockToken(uint256)", 99)); + Assert.equal(success, false, "Lock non-exist reverts"); + } + + function testLockTokenRevertAlreadyLocked() public { + ct.mint(user, 1, 10, ""); + ct.lockToken(1); + (bool success, ) = address(ct).call(abi.encodeWithSignature("lockToken(uint256)", 1)); + Assert.equal(success, false, "Lock already locked reverts"); + } + + function testLockTokenRevertNoSupply() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("lockToken(uint256)", 1)); + Assert.equal(success, false, "Lock no supply reverts"); + } + + function testLockTokenOnlyOwner() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("lockToken(uint256)", 1)); + Assert.equal(success, false, "Lock no supply fails"); // Test with supply + } + + // mint Test + function testMint() public { + ct.mint(user, 1, 10, ""); + Assert.equal(ct.balanceOf(user, 1), 10, "User balance 10"); + (,uint currentSupply,,) = ct.tokens(1); + Assert.equal(currentSupply, 10, "Token supply 10"); + } + + function testMintRevertLockedContract() public { + ct.lockContract(); + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 1, 10, "")); + Assert.equal(success, false, "Mint locked contract reverts"); + } + + function testMintRevertMintingDisabled() public { + ct.toggleMinting(false); + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 1, 10, "")); + Assert.equal(success, false, "Mint disabled reverts"); + } + + function testMintRevertInvalidCaller() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 1, 10, "")); + Assert.equal(success, true, "Mint from owner succeeds"); + } + + function testMintRevertNonExist() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 99, 10, "")); + Assert.equal(success, false, "Mint non-exist reverts"); + } + + function testMintRevertLockedToken() public { + ct.mint(user, 1, 10, ""); + ct.lockToken(1); + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 1, 5, "")); + Assert.equal(success, false, "Mint locked token reverts"); + } + + function testMintRevertDisabledToken() public { + ct.updateToken(1, false, 0); + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 1, 10, "")); + Assert.equal(success, false, "Mint disabled token reverts"); + } + + function testMintRevertSupplyExceeded() public { + ct.updateToken(1, true, 5); + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 1, 10, "")); + Assert.equal(success, false, "Mint supply exceeded reverts"); + } + + function testMintRevertZeroAmount() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("mint(address,uint256,uint256,bytes)", user, 1, 0, "")); + Assert.equal(success, false, "Mint zero amount reverts"); + } + + // _update Test (via transfer/burn) + function testUpdateMint() public { + ct.mint(user, 1, 10, ""); + (,uint currentSupply,,) = ct.tokens(1); + Assert.equal(currentSupply, 10, "Supply after mint"); + } + + function testUpdateBurn() public { + ct.mint(user, 1, 10, ""); + (bool success, ) = address(ct).call(abi.encodeWithSignature("safeTransferFrom(address,address,uint256,uint256,bytes)", user, address(0), 1, 5, "")); + Assert.equal(success, true, "Burn succeeds"); + (,uint currentSupply,,) = ct.tokens(1); + Assert.equal(currentSupply, 5, "Supply after burn"); + } + + function testUpdateTransfer() public { + ct.mint(user, 1, 10, ""); + (bool success, ) = address(ct).call(abi.encodeWithSignature("safeTransferFrom(address,address,uint256,uint256,bytes)", user, recipient, 1, 5, "")); + Assert.equal(success, true, "Transfer succeeds"); + (,uint currentSupply,,) = ct.tokens(1); + Assert.equal(currentSupply, 10, "Supply unchanged on transfer"); + } + + // uri Test + function testUri() public { + string memory json = Base64.encode(bytes('{"name":"Test Token","description":"Test Description","image":"https://test.com/image.png","properties":{}}')); + string memory expected = string.concat("data:application/json;base64,", json); + Assert.equal(ct.uri(1), expected, "URI matches"); + } + + function testUriRevertNonExist() public { + (bool success, ) = address(ct).call(abi.encodeWithSignature("uri(uint256)", 99)); + Assert.equal(success, false, "URI non-exist reverts"); + } +} diff --git a/eth/nft/tests/NFTMinter_test.sol b/eth/nft/tests/NFTMinter_test.sol new file mode 100644 index 0000000000..c6393f5f2b --- /dev/null +++ b/eth/nft/tests/NFTMinter_test.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +// This import is automatically injected by Remix +import "remix_tests.sol"; + +// This import is required to use custom transaction context +// Although it may fail compilation in 'Solidity Compiler' plugin +// But it will work fine in 'Solidity Unit Testing' plugin +import "remix_accounts.sol"; +import "../contracts/NFTMinter.sol"; +import "../contracts/NFTNumbered.sol"; + +// File name has to end with '_test.sol', this file can contain more than one testSuite contracts +contract NFTMinterTest { + NFTNumbered s; + NFTMinter m; + address public owner = address(this); + + function beforeAll() public { + s = new NFTNumbered( + "SimpleX NFT: SMPX testnet access", + "SIMPLEXNFT", + "https://ipfs.io/ipfs/abcd" + ); + m = new NFTMinter(address(s), 0, 0, false); + } + + function testCreateMinter() public { + Assert.equal(address(m.nft()), address(s), "bad nft contract"); + Assert.equal(m.mintEndTime(), 0, "bad time"); + Assert.equal(m.owner(), owner, "bad owner"); + } + + function testMinting() public { + m.setMintStartTime(block.timestamp + 86400); + try m.mint() { + Assert.ok(false, "expected revert"); + } catch Error(string memory reason) { + Assert.equal(reason, "Minting not started", "bad reason"); + } catch (bytes memory) { + Assert.ok(false, "unexpected error"); + } + m.setMintStartTime(0); + } +} diff --git a/eth/nft/tests/NFTNumbered_test.sol b/eth/nft/tests/NFTNumbered_test.sol new file mode 100644 index 0000000000..8c2f8fb51f --- /dev/null +++ b/eth/nft/tests/NFTNumbered_test.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; +import "remix_tests.sol"; +import "remix_accounts.sol"; +import "../contracts/NFTNumbered.sol"; + +contract NFTNumberedTest { + NFTNumbered s; + address public owner = address(this); + address public user1 = address(0x1); + address public user2 = address(0x2); + address public user3 = address(0x3); + address public user4 = TestsAccounts.getAccount(1); + address public minter = address(0x5); + + function beforeAll () public { + s = new NFTNumbered( + "SimpleX NFT: SMPX testnet access", + "SIMPLEXNFT", + "https://ipfs.io/ipfs/abcd" + ); + } + + function testCreateToken () public { + Assert.equal(s.name(), "SimpleX NFT: SMPX testnet access", "bad name"); + Assert.equal(s.symbol(), "SIMPLEXNFT", "bad symbol"); + Assert.equal(s.nextTokenId(), 1, "bad next token ID"); + Assert.equal(s.minter(), s.owner(), "minter different from owner"); + Assert.equal(s.mintingLocked(), false, "minting locked"); + Assert.equal(s.owner(), owner, "bad owner"); + } + + function testTransferToken() public { + Assert.equal(s.balanceOf(user3), 0, "bad balance"); + s.mint(user4); + Assert.equal(s.balanceOf(user4), 1, "bad balance"); + /// #sender: account-1 + // try s.safeTransferFrom(user4, user3, 1) { + // Assert.ok(false, "expected revert"); + // } catch Error(string memory reason) { + // Assert.equal(reason, "Token is soulbound: transfers are prohibited", "bad reason"); + // } catch (bytes memory data) { + // Assert.ok(false, "unexpected error 2"); + // } + } + + function testMinting () public { + s.mint(user1); + Assert.equal(s.balanceOf(user1), 1, "bad balance"); + Assert.equal(s.tokenURI(2), "https://ipfs.io/ipfs/abcd", "bad URI"); + try s.mint(user1) { + Assert.ok(false, "expected revert"); + } catch Error(string memory reason) { + Assert.equal(reason, "Soulbound token: only 1 per address", "bad reason"); + } catch (bytes memory) { + Assert.ok(false, "unexpected error"); + } + Assert.equal(s.nextTokenId(), 3, "bad next token ID"); + s.mint(user2); + Assert.equal(s.balanceOf(user2), 1, "bad balance"); + Assert.equal(s.tokenURI(3), "https://ipfs.io/ipfs/abcd", "bad URI"); + Assert.equal(s.nextTokenURI(), "https://ipfs.io/ipfs/abcd", "bad URI"); + s.setNextTokenURI("https://ipfs.io/ipfs/efgh"); + Assert.equal(s.nextTokenURI(), "https://ipfs.io/ipfs/efgh", "bad URI"); + Assert.equal(s.balanceOf(user3), 0, "bad balance"); + s.mint(user3); + Assert.equal(s.balanceOf(user3), 1, "bad balance"); + Assert.equal(s.tokenURI(4), "https://ipfs.io/ipfs/efgh", "bad URI"); + } + + function testMintingLock () public { + s.lockMintingPermanently(); + try s.mint(user2) { + Assert.ok(false, "expected revert"); + } catch Error(string memory reason) { + Assert.equal(reason, "Contract permanently locked for changes and minting", "bad reason"); + } catch (bytes memory) { + Assert.ok(false, "unexpected error"); + } + } +} diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 927ea5c30b..7f82f38c95 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -6,7 +6,7 @@ "https://github.com/simplex-chat/aeson.git"."aab7b5a14d6c5ea64c64dcaee418de1bb00dcc2b" = "0jz7kda8gai893vyvj96fy962ncv8dcsx71fbddyy8zrvc88jfrr"; "https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj"; "https://github.com/simplex-chat/android-support.git"."9aa09f148089d6752ce563b14c2df1895718d806" = "0pbf2pf13v2kjzi397nr13f1h3jv0imvsq8rpiyy2qyx5vd50pqn"; - "https://github.com/simplex-chat/zip.git"."bd421c6b19cc4c465cd7af1f6f26169fb8ee1ebc" = "1csqfjhvc8wb5h4kxxndmb6iw7b4ib9ff2n81hrizsmnf45a6gg0"; + "https://github.com/simplex-chat/zip.git"."2eff156c3aac389e35d38bf10a52733d7061640a" = "052vahd5d4lxnazjrb6l60i261aycn2js7jhzafyb72n15ns4r6p"; "https://github.com/yesodweb/wai.git"."ec5e017d896a78e787a5acea62b37a4e677dec2e" = "1ckcpmpjfy9jiqrb52q20lj7ln4hmq9v2jk6kpkf3m68c1m9c2bx"; "https://github.com/simplex-chat/wai.git"."2f6e5aa5f05ba9140ac99e195ee647b4f7d926b0" = "199g4rjdf1zp1fcw8nqdsyr1h36hmg424qqx03071jk7j00z7ay4"; } diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 3d3cf75713..a731f14eb9 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 6.5.0.3 +version: 6.5.0.4 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat @@ -311,7 +311,7 @@ library , unliftio-core ==0.2.* , uri-bytestring >=0.3.3.1 && <0.4 , uuid ==1.3.* - , zip ==2.0.* + , zip ==2.2.* , zstd ==0.1.3.* default-language: Haskell2010 if flag(swift) diff --git a/website/.eleventy.js b/website/.eleventy.js index a0a35f3366..aa5b03a567 100644 --- a/website/.eleventy.js +++ b/website/.eleventy.js @@ -53,7 +53,7 @@ const globalConfig = { } const translationsDirectoryPath = './langs' -const supportedRoutes = ["blog", "contact", "invitation", "docs", "fdroid", ""] +const supportedRoutes = ["blog", "contact", "invitation", "messaging", "docs", "fdroid", ""] let supportedLangs = [] fs.readdir(translationsDirectoryPath, (err, files) => { if (err) { @@ -69,6 +69,13 @@ fs.readdir(translationsDirectoryPath, (err, files) => { const translations = require("./translations.json") module.exports = function (ty) { + // Add this after your markdownLib definition + ty.addShortcode("mdInclude", function (filepath) { + const fullPath = path.join(__dirname, 'src/_includes', filepath); + const content = fs.readFileSync(fullPath, 'utf8'); + return markdownLib.render(content); + }); + ty.addShortcode("cfg", (name) => globalConfig[name]) ty.addFilter("getlang", (path) => { @@ -106,9 +113,10 @@ module.exports = function (ty) { allContentNodes.forEach((node) => { const regex = new RegExp(`(?${term.term}` const beforeContent = node.innerHTML - node.innerHTML = node.innerHTML.replace(regex, replacement) + node.innerHTML = node.innerHTML.replace(regex, (match) => { + return `${match}` + }) if (beforeContent !== node.innerHTML && !changeNoted) { changeNoted = true } diff --git a/website/README.md b/website/README.md new file mode 100644 index 0000000000..5a5155a917 --- /dev/null +++ b/website/README.md @@ -0,0 +1,9 @@ +# SimpleX Chat website + +## License + +SimpleX Chat website code is licensed under the GNU Affero General Public License version 3 (AGPLv3). See the [LICENSE](../LICENSE) file for details. + +The SimpleX and SimpleX Chat name, logo, and associated branding materials are not covered by this license and are subject to the terms outlined in the [TRADEMARK](./docs/TRADEMARK.md) file. + +Graphic designs, artworks and layouts are not licensed for re-use. If you want to use them in your publications, please ask for permission. Texts can be used as direct quotes, referencing the source. diff --git a/website/langs/ar.json b/website/langs/ar.json index 022bb6a94f..7467f657a9 100644 --- a/website/langs/ar.json +++ b/website/langs/ar.json @@ -80,13 +80,13 @@ "hero-overlay-card-1-p-4": "هذا التصميم يمنع تسريب أي البيانات الوصفية للمستخدمين على مستوى التطبيق. لزيادة تحسين الخصوصية وحماية عنوان IP الخاص بك، يمكنك الاتصال بخوادم المراسلة عبر Tor.", "hero-overlay-card-1-p-5": "الأجهزة العميلة فقط هي التي تخزن ملفات تعريف المستخدمين، جهات الاتصال والمجموعات؛ يتم إرسال الرسائل بتعمية ثنائية الطبقة بين الطرفين.", "hero-overlay-card-2-p-2": "يمكنهم بعد ذلك ربط هذه المعلومات بالشبكات الاجتماعية العامة الحالية، وتحديد بعض الهويات الحقيقية.", - "simplex-network-overlay-card-1-li-3": "P2P لا يحل مشكلة هجوم الوسيط (MITM)، ومعظم التطبيقات الحالية لا تستخدم خارج النطاق رسائل للتبادل الأولي للمفاتيح. يستخدم SimpleX رسائل خارج النطاق أو، في بعض الحالات، اتصالات آمنة وموثوق بها موجودة مسبقًا لتبادل المفاتيح الأولي.", - "privacy-matters-overlay-card-1-p-3": "تستخدم بعض الشركات المالية والتأمين رسوماً بيانية اجتماعية لتحديد أسعار الفائدة وأقساط التأمين. عادةً ما تجعل الأشخاص ذوي الدخل المنخفض يدفعون أكثر — أو كما يسمى \"علاوة الفقر\".", + "simplex-network-overlay-card-1-li-3": "P2P لا يحل مشكلة هجوم الوسيط (MITM)، ومعظم التطبيقات الحالية لا تستخدم خارج النطاق رسائل للتبادل الأولي للمفاتيح. يستخدم SimpleX رسائل خارج النطاق أو، في بعض الحالات، اتصالات آمنة وموثوق بها موجودة مسبقًا لتبادل المفاتيح الأولي.", + "privacy-matters-overlay-card-1-p-3": "تستخدم بعض الشركات المالية والتأمين رسوماً بيانية اجتماعية لتحديد أسعار الفائدة وأقساط التأمين. عادةً ما تجعل الأشخاص ذوي الدخل المنخفض يدفعون أكثر — أو كما يسمى \"علاوة الفقر\".", "privacy-matters-overlay-card-1-p-2": "يعرف تجار التجزئة عبر الإنترنت أن الأشخاص ذوي الدخل المنخفض هم أكثر عرضة لإجراء عمليات شراء عاجلة، لذلك قد يفرضون أسعارًا أعلى أو يزيلون الخصومات.", "simplex-private-6-title": "تبديل
خارج النطاق", "simplex-private-9-title": "قوائم انتظار
أحادية الاتجاه", "privacy-matters-overlay-card-1-p-4": "تحمي شبكة SimpleX خصوصية اتصالاتك بشكل أفضل من أي بديل آخر، مما يمنع تمامًا الرسم البياني الاجتماعي الخاص بك من أن يصبح متاحًا لأي شركات أو مؤسسات. حتى عندما يستخدم الأشخاص الخوادم التي توفرها SimpleX Chat، فإننا لا نعرف عدد المستخدمين أو اتصالاتهم.", - "simplex-private-card-1-point-1": "بروتوكول السقاطة المزدوجة —
رسائل OTR مع السرية المستمرة واستعادة الاختراق.", + "simplex-private-card-1-point-1": "بروتوكول Double-ratchet —
رسائل OTR مع السرية المستمرة واستعادة الاختراق.", "simplex-private-card-1-point-2": "NaCL cryptobox في كل قائمة انتظار لمنع ارتباط حركة مرور البيانات بين قوائم انتظار الرسائل في حالة اختراق TLS.", "simplex-private-card-2-point-1": "طبقة إضافية من تعمية الخادم للتسليم إلى المُستلم، لمنع الارتباط بين حركة مرور بيانات الخادم المُستلمة والمُرسلة في حالة اختراق TLS.", "simplex-private-card-4-point-2": "لاستخدام SimpleX عبر تور، يُرجى تثبيت تطبيق Orbot وتمكّين وكيل SOCKS5 (أو VPN على iOS ).", @@ -100,16 +100,16 @@ "hero-overlay-card-1-p-3": "أنت تحدد الخادم (الخوادم) المراد استخدامه لاستلام الرسائل وجهات اتصالك — الخوادم التي تستخدمها لإرسال الرسائل إليهم. من المرجح أن تستخدم كل مُحادثة خادمين مختلفين.", "hero-overlay-card-1-p-1": "سأل العديد من المستخدمين: إذا لم يكن لدى SimpleX معرّفات مستخدم، فكيف يمكنها معرفة مكان تسليم الرسائل؟ ", "hero-overlay-card-1-p-2": "لتسليم الرسائل، بدلاً من معرّفات المُستخدم المُستخدمة من قِبل جميع الشبكات الأخرى، يستخدم SimpleX معرّفات زوجية مجهولة مؤقتة لقوائم الرسائل، منفصلة لكل اتصال من اتصالاتك — ولا توجد معرّفات طويلة الأجل.", - "simplex-network-overlay-card-1-p-1": "بروتوكولات المُراسلة والتطبيقات P2P بها مشاكل مختلفة تجعلها أقل موثوقية من SimpleX وأكثر تعقيدًا في التحليل و عرضة لعدة أنواع من الهجمات.", + "simplex-network-overlay-card-1-p-1": "بروتوكولات المُراسلة والتطبيقات P2P بها مشاكل مختلفة تجعلها أقل موثوقية من SimpleX وأكثر تعقيدًا في التحليل و عرضة لعدة أنواع من الهجمات.", "hero-overlay-card-2-p-1": "عندما يكون لدى المستخدمين هويات ثابتة، حتى لو كان هذا مجرد رقم عشوائي، مثل معرّف الجلسة، فهناك خطر يتمثل في أن الموفر أو المهاجم يمكنه مراقبة كيفية اتصال المستخدمين وعدد الرسائل التي يرسلونها.", - "hero-overlay-card-1-p-6": "اقرأ المزيد في SimpleX whitepaper .", + "hero-overlay-card-1-p-6": "اقرأ المزيد في SimpleX whitepaper.", "hero-overlay-card-2-p-3": "حتى مع معظم التطبيقات الخاصة التي تستخدم خدمات Tor v3، إذا تحدثت إلى جهتي اتصال مختلفتين عبر نفس الملف الشخصي، فيمكنهما إثبات أنهما متصلان بنفس الشخص.", "hero-overlay-card-2-p-4": "يحمي SimpleX من هذه الهجمات من خلال عدم وجود أي معرّفات مستخدم في تصميمه. وإذا كنت تستخدم وضع التخفي، فسيكون لديك اسم عرض مختلف لكل جهة اتصال، مع تجنب أي بيانات مشتركة بينهما.", - "simplex-network-overlay-card-1-li-6": "قد تكون شبكات P2P عرضة هجوم DRDoS، عندما يتمكّن العملاء من إعادة بث حركة مرور البيانات وتضخيمها، مما يؤدي إلى رفض الخدمة على مستوى الشبكة. يقوم عملاء SimpleX فقط بترحيل حركة مرور البيانات من اتصال معروف ولا يمكن للمهاجم استخدامها لتضخيم حركة مرور البيانات في الشبكة بالكامل.", - "simplex-network-overlay-card-1-li-1": "تعتمد شبكات P2P على بعض المتغيرات من DHT لتوجيه الرسائل. يجب أن توازن تصميمات DHT بين ضمان التسليم وزمن الوصول. تتمتع SimpleX بضمان تسليم أفضل وزمن انتقال أقل من P2P، لأنه يمكّن تمرير الرسالة بشكل متكرر عبر عدة خوادم بالتوازي، باستخدام الخوادم التي اختارها المُستلم. في شبكات P2P، يتم تمرير الرسالة عبر عُقد O (log N) بالتسلسل، باستخدام العُقد التي تختارها الخوارزمية.", + "simplex-network-overlay-card-1-li-6": "قد تكون شبكات P2P عرضة DRDoS attack، عندما يتمكّن العملاء من إعادة بث حركة مرور البيانات وتضخيمها، مما يؤدي إلى رفض الخدمة على مستوى الشبكة. يقوم عملاء SimpleX فقط بترحيل حركة مرور البيانات من اتصال معروف ولا يمكن للمهاجم استخدامها لتضخيم حركة مرور البيانات في الشبكة بالكامل.", + "simplex-network-overlay-card-1-li-1": "تعتمد شبكات P2P على بعض المتغيرات من DHT لتوجيه الرسائل. يجب أن توازن تصميمات DHT بين ضمان التسليم وزمن الوصول. تتمتع SimpleX بضمان تسليم أفضل وزمن انتقال أقل من P2P، لأنه يمكّن تمرير الرسالة بشكل متكرر عبر عدة خوادم بالتوازي، باستخدام الخوادم التي اختارها المُستلم. في شبكات P2P، يتم تمرير الرسالة عبر عُقد O (log N) بالتسلسل، باستخدام العُقد التي تختارها الخوارزمية.", "simplex-network-overlay-card-1-li-2": "لا يحتوي تصميم SimpleX، عكس معظم شبكات P2P، على معرّفات مستخدم عالمية من أي نوع، حتى مؤقتة، ويستخدم فقط المعرّفات المزدوجة المؤقتة، مما يوفر إخفاء هوية أفضل وحماية للبيانات الوصفية.", - "simplex-network-overlay-card-1-li-4": "يمكن لبعض مزودي خدمة الإنترنت حظر تطبيقات P2P (مثل BitTorrent ). SimpleX حيادي النقل - يمكنه العمل عبر بروتوكولات الويب القياسية، على سبيل المثال WebSockets.", - "simplex-network-overlay-card-1-li-5": "قد تكون جميع شبكات P2P المعروفة عرضة لهجوم Sybil، لأن كل عقدة قابلة للاكتشاف، وتعمل الشبكة ككل. تتطلب الإجراءات المعروفة لتخفيفها إما مكونًا مركزيًا أو إثبات عمل مكلف . لا تحتوي شبكة SimpleX على إمكانية اكتشاف الخادم، فهي مجزأة وتعمل كشبكات فرعية متعددة ومعزولة، مما يجعل الهجمات على مستوى الشبكة مستحيلة.", + "simplex-network-overlay-card-1-li-4": "يمكن لبعض مزودي خدمة الإنترنت حظر تطبيقات P2P (مثل BitTorrent). SimpleX حيادي النقل - يمكنه العمل عبر بروتوكولات الويب القياسية، على سبيل المثال WebSockets.", + "simplex-network-overlay-card-1-li-5": "قد تكون جميع شبكات P2P المعروفة عرضة Sybil attack، لأن كل عقدة قابلة للاكتشاف، وتعمل الشبكة ككل. تتطلب الإجراءات المعروفة لتخفيفها إما مكونًا مركزيًا أو إثبات عمل مكلف . لا تحتوي شبكة SimpleX على إمكانية اكتشاف الخادم، فهي مجزأة وتعمل كشبكات فرعية متعددة ومعزولة، مما يجعل الهجمات على مستوى الشبكة مستحيلة.", "privacy-matters-overlay-card-3-p-1": "يجب على الجميع الاهتمام بخصوصية وأمان اتصالاتهم — يمكن للمُحادثات غير الضارة أن تعرضك للخطر، حتى لو لم يكن لديك ما تخفيه.", "privacy-matters-overlay-card-3-p-4": "لا يكفي استخدام برنامج مُراسلة مُعمَّاة بين الطرفين، يجب علينا جميعًا استخدام برامج مُراسلة التي تحمي خصوصية شبكاتنا الشخصية — مع من نحن مرتبطون.", "simplex-unique-overlay-card-1-p-3": "يحمي هذا التصميم خصوصية الأشخاص الذين تتواصل معهم، ويخفيها عن خوادم شبكة SimpleX ومن أي مراقبين. لإخفاء عنوان IP الخاص بك من الخوادم، يمكنك الاتصال بخوادم SimpleX عبر تور.", @@ -118,22 +118,22 @@ "simplex-unique-overlay-card-3-p-2": "يتم الاحتفاظ بالرسائل المُعمَّاة بين الطرفين مؤقتًا على خوادم ترحيل SimpleX حتى يتم استلامها، ثُمَّ تُحذف نهائيًا.", "simplex-unique-overlay-card-3-p-4": "لا توجد معرفّات أو نص مُعَمَّى مشترك بين حركة مرور بيانات الخادم المُرسلة والمُستلمة — إذا كان أي شخص يراقب ذلك، فلن يتمكّن بسهولة من تحديد من يتواصل مع من، حتى لو اختُرق TLS.", "simplex-unique-card-1-p-1": "يحمي SimpleX خصوصية ملف تعريفك، جهات اتصالك والبيانات الوصفية، ويخفيه عن خوادم شبكة SimpleX وأي مراقبين.", - "privacy-matters-overlay-card-2-p-1": "منذ وقت ليس ببعيد، لاحظنا أن الانتخابات الرئيسية يتم التلاعب بها بواسطة شركة استشارية ذات سمعة طيبة التي استخدمت الرسوم البيانية الاجتماعية لتشويه نظرتنا للعالم الحقيقي والتلاعب بأصواتنا.", + "privacy-matters-overlay-card-2-p-1": "منذ وقت ليس ببعيد، لاحظنا أن الانتخابات الرئيسية يتم التلاعب بها بواسطة شركة استشارية ذات سمعة طيبة التي استخدمت الرسوم البيانية الاجتماعية لتشويه نظرتنا للعالم الحقيقي والتلاعب بأصواتنا.", "privacy-matters-overlay-card-2-p-2": "لكي تكون موضوعيًا وتتخذ قرارات مستقلة، عليك التحكم في مساحة معلوماتك. لا يمكن تحقيق ذلك إلا باستخدام شبكة اتصال خاصة لا يمكنها الوصول إلى حسابك على مواقع التواصل الاجتماعي.", "privacy-matters-overlay-card-2-p-3": "SimpleX هي أول شبكة لا تحتوي على أي معرّفات مستخدم من حيث التصميم، وبهذه الطريقة تحمي مخطط اتصالاتك بشكل أفضل من أي بديل معروف.", - "privacy-matters-overlay-card-3-p-2": "واحدة من أكثر القصص إثارة للصدمة هي تجربة محمدو ولد صلاحي الموصوفة في مذكراته والموضحة في فيلم موريتاني. تم وضعه في معتقل غوانتانامو بدون محاكمة، وتعرض للتعذيب هناك لمدة 15 عامًا بعد مكالمة هاتفية مع قريبه في أفغانستان، للاشتباه في تورطه في هجمات 11 سبتمبر، على الرغم من أنه عاش في ألمانيا طوال السنوات العشر الماضية.", - "privacy-matters-overlay-card-3-p-3": "يتم القبض على الأشخاص العاديين بسبب ما يشاركونه عبر الإنترنت، حتى عبر حساباتهم \"المجهولة\"، وحتى في البلدان الديمقراطية.", + "privacy-matters-overlay-card-3-p-2": "واحدة من أكثر القصص إثارة للصدمة هي تجربة محمدو ولد صلاحي الموصوفة في مذكراته والموضحة في فيلم موريتاني. تم وضعه في معتقل غوانتانامو بدون محاكمة، وتعرض للتعذيب هناك لمدة 15 عامًا بعد مكالمة هاتفية مع قريبه في أفغانستان، للاشتباه في تورطه في هجمات 11 سبتمبر، على الرغم من أنه عاش في ألمانيا طوال السنوات العشر الماضية.", + "privacy-matters-overlay-card-3-p-3": "يتم القبض على الأشخاص العاديين بسبب ما يشاركونه عبر الإنترنت، حتى عبر حساباتهم \"المجهولة\"، وحتى في البلدان الديمقراطية.", "simplex-unique-overlay-card-1-p-1": "على عكس شبكات المُراسلة الأخرى، لا يحتوي SimpleX على معرّفات مخصصة للمستخدمين. لا يعتمد على أرقام الهواتف أو العناوين المستندة إلى النطاقات (مثل البريد الإلكتروني أو XMPP)، أسماء المستخدمين، المفاتيح العامة أو حتى الأرقام العشوائية لتحديد مستخدميها — مُشغلي خادم SimpleX لا يعرفون عدد الأشخاص الذين يستخدمون خوادمهم.", - "simplex-unique-overlay-card-1-p-2": "لتسليم الرسائل، يستخدم SimpleX العناوين المزدوجة المجهولة لقوائم انتظار الرسائل أحادية الاتجاه، منفصلة عن الرسائل المُستلمة والمُرسلة، عادةً عبر خوادم مختلفة.", + "simplex-unique-overlay-card-1-p-2": "لتسليم الرسائل، يستخدم العناوين المزدوجة المجهولة لقوائم انتظار الرسائل أحادية الاتجاه، منفصلة عن الرسائل المُستلمة والمُرسلة، عادةً عبر خوادم مختلفة.", "simplex-unique-overlay-card-3-p-1": "يخزن SimpleX Chat جميع بيانات المستخدم على أجهزة العميل فقط باستخدام تنسيق قاعدة بيانات محمولة مُعمَّاة يمكّن تصديرها ونقلها إلى أي جهاز مدعوم.", "simplex-unique-overlay-card-3-p-3": "على عكس خوادم الشبكات الاتحادية (البريد الإلكتروني أو XMPP أو Matrix)، لا تقوم خوادم SimpleX بتخزين حسابات المستخدمين، فهي تقوم فقط بترحيل الرسائل، مما يحمي خصوصية كلا الطرفين.", "simplex-unique-overlay-card-4-p-1": "يمكنك استخدام SimpleX مع خوادمك والاستمرار في التواصل مع الأشخاص الذين يستخدمون الخوادم المُهيأة مسبقًا في التطبيقات.", - "simplex-unique-overlay-card-4-p-3": "إذا كنت تفكر في التطوير شبكة SimpleX، على سبيل المثال، بوت الدردشة لمستخدمي تطبيق SimpleX، أو دمج مكتبة SimpleX Chat في تطبيقات الأجهزة المحمولة، من فضلك تواصل معي لأي نصيحة والدعم.", + "simplex-unique-overlay-card-4-p-3": "إذا كنت تفكر في التطوير شبكة SimpleX، على سبيل المثال، بوت الدردشة لمستخدمي تطبيق SimpleX، أو دمج مكتبة SimpleX Chat في تطبيقات الأجهزة المحمولة، من فضلك تواصل معي لأي نصيحة والدعم.", "donate-here-to-help-us": "تبرّع هنا لمساعدتنا", "sign-up-to-receive-our-updates": "اشترك للحصول على آخر مستجداتنا", "enter-your-email-address": "أدخل عنوان بريدك الإلكتروني", "get-simplex": "احصل على تطبيق سطح المكتب SimpleX", - "why-simplex-is-unique": "لماذا SimpleX فريد من نوعه", + "why-simplex-is-unique": "لماذا SimpleX فريد من نوعه", "learn-more": "اقرأ أكثر", "more-info": "معلومات أكثر", "hide-info": "معلومات أقل", @@ -181,10 +181,10 @@ "the-instructions--source-code": "للتعليمات حول كيفية تنزيله أو تجميعه من التعليمات البرمجية المصدر.", "if-you-already-installed-simplex-chat-for-the-terminal": "إذا قمت بالفعل بتثبيت SimpleX Chat للوحدة الطرفية", "simplex-chat-for-the-terminal": "SimpleX Chat للوحدة الطرفية", - "privacy-matters-section-header": "لماذا الخصوصية مهمة", + "privacy-matters-section-header": "لماذا الخصوصية مهمة", "privacy-matters-section-label": "تأكّد من أن برنامج المُراسلة الخاص بك لا يمكنه الوصول إلى بياناتك!", "tap-to-close": "انقر للإغلاق", - "simplex-network-section-header": "شبكة SimpleX ", + "simplex-network-section-header": "شبكة SimpleX", "simplex-network-1-header": "على عكس شبكات P2P", "simplex-network-3-header": "شبكة SimpleX", "protocol-1-text": "Signal، منصات كبيرة", @@ -192,23 +192,23 @@ "simplex-unique-card-1-p-2": "على عكس أي شبكة مُراسلة آخر، لا يحتوي SimpleX على معرّفات مخصّصة للمستخدمين — ولا حتى أرقام عشوائية.", "simplex-unique-card-2-p-1": "نظرًا لعدم وجود معرّف أو عنوان ثابت على شبكة SimpleX، لا يمكن لأي شخص الاتصال بك ما لم تشارك عنوان مستخدم لمرة واحدة أو مؤقتًا، كرمز QR أو رابط.", "simplex-unique-card-3-p-2": "يتم الاحتفاظ بالرسائل المُعمَّاة بين الطرفين مؤقتًا على خوادم ترحيل SimpleX حتى يتم استلامها، ثُمَّ تُحذف نهائيًا.", - "tap-the-connect-button-in-the-app": "اضغط على زر \"اتصال\" في التطبيق", + "tap-the-connect-button-in-the-app": "اضغط على زر \"اتصل\" في التطبيق", "scan-the-qr-code-with-the-simplex-chat-app": "امسح رمز QR باستخدام تطبيق SimpleX Chat", "scan-the-qr-code-with-the-simplex-chat-app-description": "لا يتم إرسال المفاتيح العامة وعنوان قائمة انتظار الرسائل في هذا الارتباط عبر الشبكة عند عرض هذه الصفحة —
فهي موجودة في جزء التجزئة لعنوان URL للرابط.", "if-you-already-installed": "إذا قمت بالفعل بتثبيت", "copy-the-command-below-text": "انسخ الأمر الذي في الاسفل واستخدمه في الدردشة:", - "simplex-private-section-header": "ما الذي يجعل SimpleX خصوصيًّا", - "privacy-matters-section-subheader": "الحفاظ على خصوصية بياناتك الوصفية — مع من تتحدث — يحميك من:", + "simplex-private-section-header": "ما الذي يجعل SimpleX خصوصيًّا", + "privacy-matters-section-subheader": "الحفاظ على خصوصية بياناتك الوصفية — مع مَن تتحدث — يحميك من:", "simplex-network-1-overlay-linktext": "مشاكل شبكات P2P", "simplex-network-section-desc": "يوفر Simplex Chat أفضل خصوصية من خلال الجمع بين مزايا P2P والشبكات الاتحادية.", "simplex-network-1-desc": "يتم إرسال جميع الرسائل عبر الخوادم، مما يوفر خصوصية أفضل للبيانات الوصفية وتسليمًا موثوقًا للرسائل غير المتزامنة، مع تجنب الكثير", "simplex-network-2-header": "على عكس الشبكات الاتحادية", - "simplex-network-3-desc": "توفر الخوادم قوائم انتظار أحادية الاتجاه لتوصيل المستخدمين، لكن ليس لديهم رؤية للرسم البياني لاتصال الشبكة — إلا للمستخدمين فقط.", + "simplex-network-3-desc": "توفر الخوادم قوائم انتظار أحادية الاتجاه لتوصيل المستخدمين، لكن ليس لديهم رؤية للرسم البياني لاتصال الشبكة — إلا للمستخدمين فقط.", "comparison-section-header": "مقارنة مع البروتوكولات الأخرى", "simplex-network-2-desc": "لا تقوم خوادم الترحيل SimpleX بتخزين ملفات تعريف المستخدمين وجهات الاتصال والرسائل التي تم تسليمها، ولا تتصل ببعضها البعض، ولا يوجد دليل خوادم.", "comparison-point-1-text": "يتطلب هوية عالمية", "protocol-3-text": "بروتوكولات P2P", - "simplex-unique-overlay-card-4-p-2": "تستخدم شبكة SimpleX بروتوكول مفتوح وتوفر SDK لإنشاء روبوتات دردشة، مما يسمح بتنفيذ الخدمات التي يمكن للمستخدمين التفاعل معها عبر تطبيقات SimpleX Chat — التي تتطلع حقًا لمعرفة خدمات SimpleX التي ستبنيها.", + "simplex-unique-overlay-card-4-p-2": "تستخدم شبكة بروتوكول مفتوح وتوفرSDK لإنشاء روبوتات دردشة، مما يسمح بتنفيذ الخدمات التي يمكن للمستخدمين التفاعل معها عبر تطبيقات SimpleX Chat — التي تتطلع حقًا لمعرفة خدمات SimpleX التي ستبنيها.", "guide-dropdown-1": "بداية سريعة", "guide-dropdown-2": "إرسال الرسائل", "guide-dropdown-3": "مجموعات سرية", @@ -240,8 +240,8 @@ "f-droid-org-repo": "مستودع F-Droid.org", "stable-versions-built-by-f-droid-org": "الإصدارات الثابتة التي تم إنشاؤها بواسطة F-Droid.org", "releases-to-this-repo-are-done-1-2-days-later": "تتم الإصدارات إلى هذا المستودع بعد عِدة أيام", - "f-droid-page-simplex-chat-repo-section-text": "لإضافته إلى عميل F-Droid، امسح رمز QR أو استخدم عنوان URL هذا:", - "f-droid-page-f-droid-org-repo-section-text": "مستودعات SimpleX Chat و F-Droid.org مبنية على مفاتيح مختلفة. للتبديل، يُرجى تصدير قاعدة بيانات الدردشة وإعادة تثبيت التطبيق.", + "f-droid-page-simplex-chat-repo-section-text": "لإضافته إلى عميل F-Droid ،امسح رمز QR أو استخدم عنوان URL هذا:", + "f-droid-page-f-droid-org-repo-section-text": "مستودعات SimpleX Chat و F-Droid.org مبنية على مفاتيح مختلفة. للتبديل، يُرجى صدّر قاعدة بيانات الدردشة وأعد تثبيت التطبيق.", "comparison-section-list-point-4a": "مُرحلات SimpleX لا يمكنها أن تتنازل عن تعمية بين الطرفين. تحقق من رمز الأمان للتخفيف من الهجوم على القناة خارج النطاق", "hero-overlay-3-title": "التقييمات الأمنية", "hero-overlay-card-3-p-2": "قامت Trail of Bits بمراجعة مكونات التشفير والشبكات الخاصة بشبكة SimpleX في نوفمبر 2022. اقرأ المزيد في الإعلان.", @@ -257,5 +257,58 @@ "hero-overlay-card-3-p-3": "قامت Trail of Bits بمراجعة التصميم التعموي لبروتوكولات شبكة SimpleX في يوليو 2024. اقرأ المزيد.", "docs-dropdown-14": "SimpleX للأعمال التجارية", "directory": "دليل", - "about-and-contact-us": "عن واتصل بنا" + "about-and-contact-us": "عن واتصل بنا", + "index-hero-h1": "كن
حراً", + "index-hero-h2": "حرية وأمن
اتصالاتك", + "index-hero-p1": "الشبكة الأولى التي تمتلك فيها هويتك وجهات اتصالك ومجموعاتك.", + "index-hero-download-desktop-btn-title": "نزّل تطبيق سطح مكتب SimpleX", + "index-testflight-title": "إصدار SimpleX التجريبي ل iOS على TestFlight", + "index-f-droid-title": "تطبيق SimpleX من خلال F-Droid", + "index-security-assessment-title": "التحقيقات الآمنية", + "index-security-review-2022-title": "تحقيق آمني 2022", + "index-security-review-2024-title": "تحقيق آمني 2024", + "index-security-audits-label": "التحقيقات
الآمنية", + "index-publications-privacy-guides-title": "توصيات Privacy Guides للمُراسلة", + "index-publications-whonix-title": "توصيات Whonix للمُراسلة", + "index-publications-heise-title": "منشورات Heise Online", + "index-publications-kuketz-title": "مراجعة بواسطة Mike Kuketz", + "index-publications-optout-title": "مقابلة بودكاست OptOut", + "worlds-most-secure-messaging": "المُراسلة الأكثر أمانًا في العالم", + "index-messaging-p1": "تتميز رسائل SimpleX بتعمية متطورة بين الطرفين.", + "index-messaging-p2": "لأمانك وخصوصيتك، لا يمكن للخوادم رؤية رسائلك ومَن تتحدث إليه.", + "index-messaging-cta": "تعرّف على المزيد حول مراسلة SimpleX", + "index-nextweb-h2": "أنت تملك
الشبكة التالية", + "index-nextweb-p1": "تأسست SimpleX على الاعتقاد بأن عليك امتلاك هويتك وجهات اتصالك ومجتمعاتك.", + "index-nextweb-p2": "شبكة مفتوحة ولامركزية تتيح لك التواصل مع الأشخاص ومشاركة الأفكار: كن حرًا وآمنًا.", + "index-token-h2": "مجتمعات تدوم", + "index-token-p1": "ستدعم مجموعاتك المفضلة بقسائم المجتمع المستقبلية.", + "index-token-p2": "ستدفع القسائم ثمن الخوادم، لتمكين مجتمعاتك من البقاء حرة ومستقلة.", + "index-roadmap-h2": "خارطة طريق SimpleX للإنترنت المجاني", + "index-roadmap-2025": "2025", + "index-roadmap-2025-title": "التوسع إلى مجتمعات كبيرة", + "index-roadmap-2025-desc": "الهروب من المنصات المركزية", + "index-roadmap-2026-title": "مجتمعات وخوادم مستدامة", + "index-roadmap-2026-desc": "إطلاق قسائم المجتمع", + "index-roadmap-2027-title": "اجعل مجتمعاتك تنمو", + "index-roadmap-2027-desc": "أدوات لتعزيز مجتمعاتك", + "index-directory-h2": "انضم إلى مجتمعات SimpleX", + "index-directory-p1": "مئات الآلاف من الأشخاص يثقون بالفعل في مُراسلة SimpleX.", + "index-directory-p2": "ابحث عن مجتمعاتك في دليل SimpleX وأنشئ مجتمعك الخاص!", + "index-directory-cta": "اعرض دليل SimpleX", + "index-directory-users-group-title": "مجموعة مستخدمي SimpleX", + "how-secure-comparison-title": "مقارنة أمان التعمية بين الطرفين في برامج المُراسلة المختلفة", + "how-secure-message-padding": "حشو الرسالة", + "how-secure-repudiation-deniability": "الإنكار (عدم القدرة على إثبات التورط)", + "how-secure-forward-secrecy": "السرية المستمرة", + "how-secure-break-in-recovery": "الأمن بعد الاختراق", + "how-secure-two-factor-key-exchange": "تبادل مفتاح ثنائي العوامل", + "how-secure-post-quantum-hybrid-crypto": "التعمية الهجينة ما بعد الكمومي", + "messengers-comparison-section-list-point-1": "يقوم Briar بحشو الرسائل إلى حجم مقرب لأعلى إلى 1024 بايت، بينما Signal يحشوها إلى 160 بايت", + "messengers-comparison-section-list-point-2": "لا يشمل الإنكار اتصال العميل بالخادم.", + "messengers-comparison-section-list-point-3": "يبدو أن استخدام التوقيعات المُعمّاة يضر بالإنكار (عدم القدرة على التنصل)، ولكن هذا يحتاج إلى توضيح.", + "messengers-comparison-section-list-point-4": "تطبيقات الأجهزة المتعددة تعرض أمن Double Ratchet بعد الاختراق للخطر", + "messengers-comparison-section-list-point-5": "تبادل المفاتيح الثنائي اختياري عبر التحقق من رمز الأمان.", + "messengers-comparison-section-list-point-6": "اتفاقية مفتاح ما بعد الكم \"متفرقة\" — فهي تحمي بعض خطوات ratchet فقط.", + "index-roadmap-2026": "2026", + "index-roadmap-2027": "2027" } diff --git a/website/langs/bg.json b/website/langs/bg.json index fe70e244d9..7661d916a3 100644 --- a/website/langs/bg.json +++ b/website/langs/bg.json @@ -21,7 +21,7 @@ "smp-protocol": "СМП Протокол", "chat-protocol": "Чат протокол", "donate": "Дарете", - "copyright-label": "© 2020-2025 SimpleX | Проект с отворен код", + "copyright-label": "© 2020-2025 SimpleX Chat | Проект с отворен код", "simplex-chat-protocol": "SimpleX Чат протокол", "terminal-cli": "Системна конзола", "terms-and-privacy-policy": "Политика за поверителност", diff --git a/website/langs/cs.json b/website/langs/cs.json index 5a7b03616b..fc609128bf 100644 --- a/website/langs/cs.json +++ b/website/langs/cs.json @@ -25,7 +25,7 @@ "smp-protocol": "SMP protokol", "chat-protocol": "Chat protokol", "donate": "Darovat", - "copyright-label": "© 2020-2025 SimpleX | Projekt s otevřeným zdrojovým kódem", + "copyright-label": "© 2020-2025 SimpleX Chat | Projekt s otevřeným zdrojovým kódem", "simplex-chat-protocol": "SimpleX Chat protokol", "terminal-cli": "Terminálové rozhraní příkazového řádku", "terms-and-privacy-policy": "Ochrana soukromí", diff --git a/website/langs/de.json b/website/langs/de.json index c3d4035374..4c5925a7b8 100644 --- a/website/langs/de.json +++ b/website/langs/de.json @@ -21,7 +21,7 @@ "smp-protocol": "SMP-Protokoll", "chat-bot-example": "Beispiel für einen Chatbot", "donate": "Spenden", - "copyright-label": "© 2020-2025 SimpleX | Open-Source-Projekt", + "copyright-label": "© 2020-2025 SimpleX Chat | Open-Source-Projekt", "chat-protocol": "Chat-Protokoll", "simplex-chat-protocol": "SimpleX-Chat-Protokoll", "terminal-cli": "Terminal-Kommandozeilen-Schnittstelle", @@ -92,20 +92,20 @@ "simplex-private-card-8-point-1": "Die SimpleX-Server arbeiten als Mix-Knoten mit geringer Verzögerung — eingehende und ausgehende Nachrichten haben eine unterschiedliche Reihenfolge.", "hero-overlay-card-1-p-3": "Sie definieren, welche(n) Server Sie für den Empfang von Nachrichten nutzen. Ihre Kontakte — nutzen diese Server, um ihnen Nachrichten darüber zu senden. Jede Konversation nutzt üblicherweise also zwei unterschiedliche Server.", "hero-overlay-card-1-p-5": "Die Benutzer-Profile, Kontakte und Gruppen werden nur auf den Endgerät des Nutzers gespeichert. Die Nachrichten werden mit einer 2-Schichten-Ende-zu-Ende-Verschlüsselung versendet.", - "hero-overlay-card-1-p-6": "Lesen Sie mehr darüber im SimpleX-Whitepaper.", + "hero-overlay-card-1-p-6": "Lesen Sie mehr darüber im SimpleX-Whitepaper.", "hero-overlay-card-2-p-2": "Sie können diese Informationen mit bestehenden öffentlichen sozialen Netzwerken korrelieren und damit wahre Identitäten herausfinden.", "hero-overlay-card-2-p-3": "Wenn Sie sich mit zwei unterschiedlichen Kontakten über dasselbe Profil unterhalten, können sie, selbst bei sehr auf Privatsphäre bedachten Apps, die Tor-v3-Dienste nutzen, feststellen, dass diese Kontakte mit derselben Person verbunden sind.", "hero-overlay-card-1-p-2": "Um Nachrichten auszuliefern, nutzt SimpleX statt Benutzerkennungen wie alle anderen Netzwerke nur temporäre, anonyme und paarweise Kennungen für Nachrichten-Warteschlangen, die für jede Ihrer Verbindungen unterschiedlich sind — es gibt keinerlei Langzeit-Kennungen.", "hero-overlay-card-1-p-4": "Dieses Design verhindert schon auf der Applikations-Ebene Datenlecks für jegliche Benutzer'Metadaten. Sie können sich über Tor mit Nachrichten-Servern verbinden, um Ihre Privatsphäre weiter zu verbessern und die von Ihnen genutzte IP-Adresse zu schützen.", "hero-overlay-card-2-p-1": "Wenn Nutzer dauerhafte Identitäten besitzen, selbst wenn diese eine Zufallsnummer, wie eine Sitzungs-ID, ist, besteht ein Risiko, das Provider oder Angreifer feststellen können, wie Nutzer miteinander verbunden sind und wie viele Nachrichten sie versenden.", "hero-overlay-card-2-p-4": "SimpleX schützt gegen solche Angriffe, weil es vom Design her keinerlei Benutzerkennungen besitzt. Und Sie haben sogar unterschiedliche Anzeigenamen für jeden Kontakt und vermeiden jegliche geteilte Daten zwischen diesen, wenn Sie den Inkognito-Modus nutzen.", - "simplex-network-overlay-card-1-p-1": "Peer-to-Peer-Nachrichten-Protokolle und -Applikationen haben verschiedene Probleme, die diese weniger vertrauenswürdig, die Analyse wesentlich komplexer und anfälliger gegen verschiedene Arten von Angriffen, als bei SimpleX machen.", + "simplex-network-overlay-card-1-p-1": "Peer-to-Peer-Nachrichten-Protokolle und -Applikationen haben verschiedene Probleme, die diese weniger vertrauenswürdig, die Analyse wesentlich komplexer und anfälliger gegen verschiedene Arten von Angriffen, als bei SimpleX machen.", "simplex-network-overlay-card-1-li-2": "Das SimpleX Design hat, im Gegensatz zu den meisten P2P-Netzwerken, keinerlei globalen Benutzerkennungen, auch keine temporären. Es nutzt ausschließlich temporäre paarweise Kennungen, die bessere Anonymität und Metadaten-Schutz bieten.", - "simplex-network-overlay-card-1-li-4": "——P2P-Implementierungen können durch Internetanbieter blockiert werden, wie beispielweise BitTorrent). SimpleX ist transportunabhängig — es kann über Standard-Web-Protokolle, wie beispielsweise WebSockets, arbeiten.", - "simplex-network-overlay-card-1-li-6": "P2P-Netzwerke können anfällig für DRDoS-Angriffe sein, wenn die Clients den Datenverkehr erneut senden und verstärken können, was zu einem netzwerkweiten Denial-of-Service führt. SimpleX-Clients leiten nur Datenverkehr von bekannten Verbindungen weiter und können von einem Angreifer nicht dazu verwendet werden, den Datenverkehr im gesamten Netzwerk zu verstärken.", + "simplex-network-overlay-card-1-li-4": "——P2P-Implementierungen können durch Internetanbieter blockiert werden, wie beispielweise BitTorrent). SimpleX ist transportunabhängig — es kann über Standard-Web-Protokolle, wie beispielsweise WebSockets, arbeiten.", + "simplex-network-overlay-card-1-li-6": "P2P-Netzwerke können anfällig für DRDoS-Angriffe sein, wenn die Clients den Datenverkehr erneut senden und verstärken können, was zu einem netzwerkweiten Denial-of-Service führt. SimpleX-Clients leiten nur Datenverkehr von bekannten Verbindungen weiter und können von einem Angreifer nicht dazu verwendet werden, den Datenverkehr im gesamten Netzwerk zu verstärken.", "privacy-matters-overlay-card-1-p-1": "Viele große Unternehmen nutzen Informationen, mit wem Sie in Verbindung stehen, um Ihr Einkommen zu schätzen, Ihnen Produkte zu verkaufen, die Sie nicht wirklich benötigen und um die Preise zu bestimmen.", "privacy-matters-overlay-card-1-p-2": "Online-Händler wissen, dass Menschen mit geringerem Einkommen eher dringende Einkäufe tätigen, sodass sie möglicherweise höhere Preise verlangen können oder Rabatte streichen.", - "privacy-matters-overlay-card-1-p-3": "Einige Finanz- und Versicherungsunternehmen verwenden soziale Graphen, um Zinssätze und Prämien zu ermitteln. Menschen mit niedrigerem Einkommen zahlen so häufig mehr — dies ist als \"Armutsprämie\" bekannt.", + "privacy-matters-overlay-card-1-p-3": "Einige Finanz- und Versicherungsunternehmen verwenden soziale Graphen, um Zinssätze und Prämien zu ermitteln. Menschen mit niedrigerem Einkommen zahlen so häufig mehr — dies ist als \"Armutsprämie\" bekannt.", "privacy-matters-overlay-card-2-p-2": "Um objektiv zu sein und unabhängige Entscheidungen treffen zu können, müssen Sie die Kontrolle über Ihren Informationsraum haben. Dies ist nur möglich, wenn Sie ein privates Kommunikations-Netzwerk verwenden, welches keinen Zugriff auf Ihren sozialen Graphen hat.", "privacy-matters-overlay-card-2-p-3": "SimpleX ist das erste Netzwerk, welches per Design keinerlei Benutzerkennungen hat und auf diese Weise Ihren Verbindungsgraphen besser schützt als jede andere bekannte Alternative.", "privacy-matters-overlay-card-3-p-1": "Jede Person sollte sich um ihre Privatsphäre und die Sicherheit ihrer Kommunikation kümmern — Harmlose Gespräche könnten Sie in Gefahr bringen, selbst wenn Sie nichts zu verbergen haben.", @@ -145,10 +145,10 @@ "the-instructions--source-code": "Für die Anleitungen, wie Sie es herunterladen oder aus dem Quellcode kompilieren.", "if-you-already-installed": "Wenn Sie es schon installiert haben", "simplex-chat-for-the-terminal": "SimpleX Chat für das Terminal", - "privacy-matters-section-header": "Warum es auf Privatsphäre ankommt", + "privacy-matters-section-header": "Warum es auf Privatsphäre ankommt", "privacy-matters-section-label": "Stellen Sie sicher, dass Ihr Messenger keinen Zugriff auf Ihre Daten hat!", "tap-to-close": "Zum Schließen drücken", - "simplex-network-section-header": "SimpleX-Netzwerk", + "simplex-network-section-header": "SimpleX-Netzwerk", "simplex-network-1-header": "Im Gegensatz zu P2P-Netzwerken", "simplex-network-1-desc": "Alle Nachrichten werden über Server versandt, was sowohl einen besseren Schutz der Metadaten als auch eine zuverlässigere asynchrone Nachrichtenübermittlung ermöglicht und dabei Vieles vermeidet", "simplex-network-1-overlay-linktext": "Probleme von P2P-Netzwerken", @@ -175,40 +175,40 @@ "comparison-section-list-point-7": "P2P-Netzwerke haben entweder eine zentrale Verwaltung oder das gesamte Netzwerk kann kompromittiert werden", "see-here": "Siehe hier", "simplex-unique-card-4-p-1": "Das SimpleX-Netzwerk ist vollständig dezentralisiert und unabhängig von Kryptowährungen oder anderen Netzwerken außer dem Internet.", - "privacy-matters-overlay-card-2-p-1": "Vor nicht allzu langer Zeit beobachteten wir, wie große Wahlen von einem angesehenen Beratungsunternehmen manipuliert wurden, welches unsere sozialen Graphen nutzte, um unsere Sicht auf die reale Welt zu verzerren und unsere Stimmen zu manipulieren.", - "privacy-matters-overlay-card-3-p-2": "Eine der schockierendsten Geschichten ist die Erfahrung von Mohamedou Ould Salahi, welche in seinen Memoiren beschrieben und im Film „The Mauritanian“ gezeigt wird. Er kam nach einem Anruf bei seinen Verwandten in Afghanistan und ohne Gerichtsverfahren in das Guantanamo-Lager und wurde dort einige Jahre lang gefoltert, weil er verdächtigt wurde, an den 9/11-Angriffen beteiligt gewesen zu sein, obwohl er die vorhergehenden 10 Jahre in Deutschland gelebt hatte.", + "privacy-matters-overlay-card-2-p-1": "Vor nicht allzu langer Zeit beobachteten wir, wie große Wahlen von einem angesehenen Beratungsunternehmen manipuliert wurden, welches unsere sozialen Graphen nutzte, um unsere Sicht auf die reale Welt zu verzerren und unsere Stimmen zu manipulieren.", + "privacy-matters-overlay-card-3-p-2": "Eine der schockierendsten Geschichten ist die Erfahrung von Mohamedou Ould Salahi, welche in seinen Memoiren beschrieben und im Film „The Mauritanian“ gezeigt wird. Er kam nach einem Anruf bei seinen Verwandten in Afghanistan und ohne Gerichtsverfahren in das Guantanamo-Lager und wurde dort einige Jahre lang gefoltert, weil er verdächtigt wurde, an den 9/11-Angriffen beteiligt gewesen zu sein, obwohl er die vorhergehenden 10 Jahre in Deutschland gelebt hatte.", "simplex-unique-overlay-card-1-p-1": "Im Gegensatz zu anderen Nachrichten-Netzwerken weist SimpleX den Benutzern keine Kennungen zu. Es verlässt sich nicht auf Telefonnummern, domänenbasierte Adressen (wie E-Mail oder XMPP), Benutzernamen, öffentliche Schlüssel oder Zufallszahlen, um seine Benutzer zu identifizieren — selbst SimpleX-Server-Betreiber wissen nicht, wie viele Personen deren Server verwenden.", - "simplex-unique-overlay-card-1-p-2": "Um Nachrichten auszuliefern nutzt SimpleX paarweise anonyme Adressen aus unidirektionalen Nachrichten-Warteschlangen, die für empfangene und gesendete Nachrichten separiert sind und gewöhnlich über verschiedene Server gesendet werden.", - "simplex-network-overlay-card-1-li-5": "Alle bekannten P2P-Netzwerke können anfällig für Sybil-Angriffe sein, da jeder Knoten ermittelbar ist und das Netzwerk als Ganzes funktioniert. Bekannte Maßnahmen zur Verhinderung erfordern entweder eine zentralisierte Komponente oder einen teuren Ausführungsnachweis. Das SimpleX-Netzwerk bietet keine Ermittlung der Server, ist fragmentiert und arbeitet mit mehreren isolierten Subnetzwerken, wodurch netzwerkweite Angriffe unmöglich werden.", - "simplex-network-overlay-card-1-li-3": "P2P löst nicht das Problem des MITM-Angriffs und die meisten bestehenden Implementierungen nutzen für den initialen Schlüsselaustausch keine Out-of-Band-Nachrichten. Im Gegensatz hierzu nutzt SimpleX für den initialen Schlüsselaustausch Out-of-Band-Nachrichten oder zum Teil schon bestehende sichere und vertrauenswürdige Verbindungen.", - "tap-the-connect-button-in-the-app": "Drücken Sie die „Verbinden“-Taste in der Applikation", + "simplex-unique-overlay-card-1-p-2": "Um Nachrichten auszuliefern nutzt SimpleX paarweise anonyme Adressen aus unidirektionalen Nachrichten-Warteschlangen, die für empfangene und gesendete Nachrichten separiert sind und gewöhnlich über verschiedene Server gesendet werden.", + "simplex-network-overlay-card-1-li-5": "Alle bekannten P2P-Netzwerke können anfällig für Sybil-Angriffe sein, da jeder Knoten ermittelbar ist und das Netzwerk als Ganzes funktioniert. Bekannte Maßnahmen zur Verhinderung erfordern entweder eine zentralisierte Komponente oder einen teuren Ausführungsnachweis. Das SimpleX-Netzwerk bietet keine Ermittlung der Server, ist fragmentiert und arbeitet mit mehreren isolierten Subnetzwerken, wodurch netzwerkweite Angriffe unmöglich werden.", + "simplex-network-overlay-card-1-li-3": "P2P löst nicht das Problem des MITM-Angriffs und die meisten bestehenden Implementierungen nutzen für den initialen Schlüsselaustausch keine Out-of-Band-Nachrichten. Im Gegensatz hierzu nutzt SimpleX für den initialen Schlüsselaustausch Out-of-Band-Nachrichten oder zum Teil schon bestehende sichere und vertrauenswürdige Verbindungen.", + "tap-the-connect-button-in-the-app": "Drücken Sie die „Verbinden“-Taste in der Applikation", "to-make-a-connection": "Um eine Verbindung zu starten:", "contact-hero-p-3": "Nutzen Sie die unten genannten Links, um die Applikation herunterzuladen.", "scan-the-qr-code-with-the-simplex-chat-app": "Scannen Sie den QR-Code mit der SimpleX-Chat-Applikation", "copy-the-command-below-text": "Kopieren Sie sich das unten genannte Kommando und nutzen Sie es im Chat:", - "privacy-matters-section-subheader": "Die Wahrung der Privatsphäre Ihrer Metadaten — mit wem Sie wann Kontakt haben — schützt Sie vor:", - "simplex-private-section-header": "Was macht SimpleX vertraulich", + "privacy-matters-section-subheader": "Die Wahrung der Privatsphäre Ihrer Metadaten — mit wem Sie wann Kontakt haben — schützt Sie vor:", + "simplex-private-section-header": "Was macht SimpleX vertraulich", "simplex-network-section-desc": "Durch die Kombination der Vorteile von P2P und föderierten Netzwerken stellt SimpleX die bestmögliche Privatsphäre zur Verfügung.", "simplex-network-2-header": "Im Gegensatz zu föderierten Netzwerken", "comparison-section-list-point-1": "Normalerweise auf der Grundlage einer Telefonnummer, in einigen Fällen auf der Grundlage von Benutzernamen", "comparison-point-5-text": "Zentrale Komponente oder andere Netzwerk-weite Angriffe", "no-decentralized": "Nein – dezentralisiert", "comparison-section-list-point-5": "Die Privatsphäre-Metadaten des Nutzers werden nicht geschützt", - "simplex-network-overlay-card-1-li-1": "P2P-Netzwerke vertrauen auf Varianten von DHT, um Nachrichten zu routen. DHT-Designs müssen zwischen Zustellungsgarantie und Latenz ausgleichen. Verglichen mit P2P bietet SimpleX sowohl eine bessere Zustellungsgarantie als auch eine niedrigere Latenz, weil eine Nachricht redundant und parallel über mehrere Server gesendet werden kann, wobei die durch den Empfänger ausgewählten Server genutzt werden. In P2P-Netzwerken werden Nachrichten sequentiell über O(log N)-Knoten gesendet, wobei die Knoten durch einen Algorithmus ausgewählt werden.", + "simplex-network-overlay-card-1-li-1": "P2P-Netzwerke vertrauen auf Varianten von DHT, um Nachrichten zu routen. DHT-Designs müssen zwischen Zustellungsgarantie und Latenz ausgleichen. Verglichen mit P2P bietet SimpleX sowohl eine bessere Zustellungsgarantie als auch eine niedrigere Latenz, weil eine Nachricht redundant und parallel über mehrere Server gesendet werden kann, wobei die durch den Empfänger ausgewählten Server genutzt werden. In P2P-Netzwerken werden Nachrichten sequentiell über O(log N)-Knoten gesendet, wobei die Knoten durch einen Algorithmus ausgewählt werden.", "simplex-unique-overlay-card-3-p-4": "Zwischen dem gesendeten und empfangenen Serververkehr gibt es keine gemeinsamen Kennungen oder Chiffriertexte — sodass ein Beobachter nicht ohne weiteres feststellen kann, wer mit wem kommuniziert, selbst wenn TLS kompromittiert wurde.", - "simplex-unique-overlay-card-4-p-3": "Falls Sie Interesse daran haben, aktiv bei der Entwicklung des SimpleX-Netzwerks mitzuhelfen, z.B. einen Chatbot für SimpleX-App-Nutzer zu entwickeln oder die Integration der SimpleX-Chat-Bibliothek in mobile Apps voranzutreiben, kontaktieren Sie uns bitte für eine weitere Beratung und Unterstützung.", + "simplex-unique-overlay-card-4-p-3": "Falls Sie Interesse daran haben, aktiv bei der Entwicklung des SimpleX-Netzwerks mitzuhelfen, z.B. einen Chatbot für SimpleX-App-Nutzer zu entwickeln oder die Integration der SimpleX-Chat-Bibliothek in mobile Apps voranzutreiben, kontaktieren Sie uns bitte für eine weitere Beratung und Unterstützung.", "privacy-matters-overlay-card-1-p-4": "Das SimpleX-Netzwerk schützt die Privatsphäre Ihrer Verbindungen besser als jede Alternative und verhindert vollständig, dass Ihr sozialer Graph für Unternehmen oder Organisationen verfügbar wird. Selbst wenn Anwender die in der SimpleX-Chat-App vorkonfigurierten Server verwenden, kennen die Server-Betreiber die Anzahl der Benutzer oder deren Verbindungen nicht.", "contact-hero-header": "Sie haben eine Adresse zur Verbindung mit SimpleX Chat erhalten", "invitation-hero-header": "Sie haben einen Einmal-Link zur Verbindung mit SimpleX Chat erhalten", - "privacy-matters-overlay-card-3-p-3": "Normale Menschen werden für das, was sie online teilen, sogar unter Nutzung ihrer „anonymen“ Konten, selbst in demokratischen Ländern verhaftet.", + "privacy-matters-overlay-card-3-p-3": "Normale Menschen werden für das, was sie online teilen, sogar unter Nutzung ihrer „anonymen“ Konten, selbst in demokratischen Ländern verhaftet.", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat speichert alle Benutzerdaten ausschließlich auf den Endgeräten in einem portablen und verschlüsselten Datenbankformat, welches exportiert und auf jedes unterstützte Gerät übertragen werden kann.", "simplex-unique-overlay-card-2-p-2": "Auch wenn die optionale Benutzeradresse zum Versenden von Spam-Kontaktanfragen verwendet werden kann, können Sie sie ändern oder ganz löschen, ohne dass Ihre Verbindungen verloren gehen.", - "simplex-unique-overlay-card-4-p-2": "Das SimpleX-Netzwerk verwendet ein offenes Protokoll und bietet ein SDK zur Erstellung von Chatbots an. Dies ermöglicht die Erstellung von Diensten, mit denen Nutzer über SimpleX-Chat-Apps interagieren können — wir sind gespannt, welche SimpleX-Dienste Sie entwickeln werden.", + "simplex-unique-overlay-card-4-p-2": "Das SimpleX-Netzwerk verwendet ein offenes Protokoll und bietet ein SDK zur Erstellung von Chatbots an. Dies ermöglicht die Erstellung von Diensten, mit denen Nutzer über SimpleX-Chat-Apps interagieren können — wir sind gespannt, welche SimpleX-Dienste Sie entwickeln werden.", "simplex-unique-card-4-p-2": "Sie können SimpleX mit Ihren eigenen Servern oder mit den von uns zur Verfügung gestellten Servern verwenden — und sich trotzdem mit jedem Benutzer verbinden.", - "why-simplex-is-unique": "Warum ist SimpleX einmalig", + "why-simplex-is-unique": "Warum ist SimpleX einmalig", "contact-hero-p-1": "Die öffentlichen Schlüssel und die Adresse der Nachrichtenwarteschlange in diesem Link werden NICHT über das Netzwerk gesendet, wenn Sie diese Seite aufrufen — sie sind in dem Hash-Fragment der Link-URL enthalten.", "if-you-already-installed-simplex-chat-for-the-terminal": "Wenn Sie SimpleX schon für das Terminal installiert haben", - "simplex-network-3-desc": "Die Server stellen unidirektionale Warteschlangen zur Verfügung, um die Benutzer miteinander zu verbinden. Sie haben aber keinen Einblick in den Verbindungs-Graphen des Netzwerks — Diesen haben nur die Benutzer selbst.", + "simplex-network-3-desc": "Die Server stellen unidirektionale Warteschlangen zur Verfügung, um die Benutzer miteinander zu verbinden. Sie haben aber keinen Einblick in den Verbindungs-Graphen des Netzwerks — Diesen haben nur die Benutzer selbst.", "guide-dropdown-1": "Schnellstart", "guide-dropdown-2": "Nachrichten senden", "guide-dropdown-4": "Chat-Profile", @@ -235,13 +235,13 @@ "signing-key-fingerprint": "Fingerabdruck des Signaturschlüssels (SHA-256)", "f-droid-org-repo": "F-Droid.org-Repository", "stable-versions-built-by-f-droid-org": "Von F-Droid.org erstellte stabile Versionen", - "f-droid-page-f-droid-org-repo-section-text": "SimpleX-Chat- und F-Droid.org-Repositorys signieren ihre Builds mit verschiedenen Schlüsseln. Zum Umschalten bitte die Chat-Datenbank exportieren und die App neu installieren.", + "f-droid-page-f-droid-org-repo-section-text": "SimpleX-Chat- und F-Droid.org-Repositorys signieren ihre Builds mit verschiedenen Schlüsseln. Zum Umschalten bitte die Chat-Datenbank exportieren und die App neu installieren.", "releases-to-this-repo-are-done-1-2-days-later": "Die Versionen für dieses Repository werden einige Tage später erstellt", "docs-dropdown-8": "SimpleX-Verzeichnis", "simplex-chat-via-f-droid": "SimpleX Chat per F-Droid", "simplex-chat-repo": "SimpleX-Chat-Repository", "stable-and-beta-versions-built-by-developers": "Von den Entwicklern erstellte stabile und Beta-Versionen", - "f-droid-page-simplex-chat-repo-section-text": "Um es Ihrem F-Droid-Client hinzuzufügen, scannen Sie den QR-Code oder nutzen Sie diese URL:", + "f-droid-page-simplex-chat-repo-section-text": "Um es Ihrem F-Droid-Client hinzuzufügen, scannen Sie den QR-Code oder nutzen Sie diese URL:", "comparison-section-list-point-4a": "SimpleX-Relais können die E2E-Verschlüsselung nicht kompromittieren. Überprüfen Sie den Sicherheitscode, um einen möglichen Angriff auf den Out-of-Band-Kanal zu entschärfen", "hero-overlay-3-title": "Security-Gutachten", "hero-overlay-card-3-p-2": "Trail of Bits untersuchte im November 2022 die kryptografischen und Netzwerk-Komponenten der SimpleX-Plattform. Lesen Sie mehr dazu in der Ankündigung.", @@ -263,27 +263,27 @@ "index-hero-p1": "Das erste Netzwerk, in dem Sie Ihre Identität, Kontakte und Gruppen besitzen.", "index-hero-download-desktop-btn-title": "Download der SimpleX Desktop-App", "index-testflight-title": "Öffentlicher iOS-Preview auf TestFlight", - "index-f-droid-title": "SimpleX F-Droid-Repository", + "index-f-droid-title": "SimpleX-App über das F-Droid-Repository", "index-security-assessment-title": "Security-Gutachten", "index-security-review-2022-title": "Security-Überprüfung 2022", "index-security-review-2024-title": "Security-Überprüfung 2024", "index-security-audits-label": "Security-
Audits", - "index-publications-privacy-guides-title": "Empfehlungen für Messenger", + "index-publications-privacy-guides-title": "Messenger-Empfehlungen von Privacy Guides", "index-publications-whonix-title": "Whonix - Empfehlungen für Messenger", - "index-publications-heise-title": "Veröffentlichung", - "index-publications-kuketz-title": "Überprüfung", - "index-publications-optout-title": "Podcast-Interview", + "index-publications-heise-title": "Veröffentlichungen von Heise Online", + "index-publications-kuketz-title": "Überprüfung von Mike Kuketz", + "index-publications-optout-title": "Podcast-Interview von OptOut", "worlds-most-secure-messaging": "Das sicherste Messaging-System der Welt", "index-messaging-p1": "SimpleX-Messaging verfügt über modernste Ende-zu-Ende-Verschlüsselung.", - "index-messaging-p2": "Zu Ihrer Sicherheit und zum Schutz Ihrer Privatsphäre können Server Ihre Nachrichten weder sehen, noch mit wem Sie kommunizieren.", + "index-messaging-p2": "Zu Ihrer Sicherheit und zum Schutz Ihrer Privatsphäre können Server Ihre Nachrichten weder sehen, noch mit wem Sie kommunizieren.", "index-messaging-cta": "Lernen Sie mehr über SimpleX-Messaging", "index-nextweb-h2": "Sie besitzen
The Next Web", - "index-nextweb-p1": "SimpleX basiert auf der Überzeugung, dass Sie Eigentümer Ihrer Identität, Ihrer Kontakte und Ihrer Communities sein müssen.", + "index-nextweb-p1": "SimpleX basiert auf der Überzeugung, dass Sie Eigentümer Ihrer Identität, Ihrer Kontakte und Ihrer Communities sein müssen.", "index-nextweb-p2": "Ein offenes und dezentrales Netzwerk ermöglicht es Ihnen, mit Menschen in Kontakt zu treten und Ideen auszutauschen: Seien Sie frei und sicher.", "index-token-h2": "Communities, die Bestand haben", "index-token-p1": "Sie werden Ihre Lieblingsgruppen mit zukünftigen Community-Gutscheinen unterstützen.", "index-token-p2": "Server werden mit Gutscheinen bezahlt, damit Ihre Communities kostenlos und unabhängig bleiben können.", - "index-token-cta": "Erfahren Sie mehr und holen Sie sich Ihren kostenlosen NFT
für 20 % Rabatt auf SimpleX-Token!", + "index-token-cta": "Erfahren Sie mehr und holen Sie sich Ihren kostenlosen NFT
zum frühzeitigen ausprobieren.", "index-roadmap-h2": "SimpleX - Roadmap zum freien Internet", "index-roadmap-2025": "2025", "index-roadmap-2025-title": "Skalierung auf große Communities", @@ -299,7 +299,7 @@ "index-directory-p2": "Finden Sie Ihre Communities im SimpleX-Verzeichnis und erstellen Sie Ihre Eigenen!", "index-directory-cta": "SimpleX-Verzeichnis anzeigen", "index-directory-users-group-title": "SimpleX-Nutzergruppe", - "how-secure-comparison-title": "Wie sicher ist die Ende-zu-Ende-Verschlüsselung in verschiedenen Messengern?", + "how-secure-comparison-title": "Sicherheitsvergleich der Ende-zu-Ende-Verschlüsselung in verschiedenen Messengern", "how-secure-message-padding": "Nachrichten-Padding", "how-secure-repudiation-deniability": "Glaubhafte Abstreitbarkeit", "how-secure-forward-secrecy": "Forward Secrecy", diff --git a/website/langs/en.json b/website/langs/en.json index eeddf9db4a..02f370f2cc 100644 --- a/website/langs/en.json +++ b/website/langs/en.json @@ -5,6 +5,7 @@ "reference": "Reference", "blog": "Blog", "features": "Features", + "navbar-token": "Token", "why-simplex": "Why SimpleX", "simplex-privacy": "SimpleX privacy", "simplex-network": "SimpleX network", @@ -22,7 +23,7 @@ "smp-protocol": "SMP protocol", "chat-protocol": "Chat protocol", "donate": "Donate", - "copyright-label": "© 2020-2025 SimpleX | Open-Source Project", + "copyright-label": "© 2020-2025 SimpleX Chat | Open-Source Project", "simplex-chat-protocol": "SimpleX Chat protocol", "terminal-cli": "Terminal CLI", "about-and-contact-us": "About & Contact us", @@ -283,7 +284,7 @@ "index-token-h2": "Communities That Last", "index-token-p1": "You will support your favorite groups with future Community Vouchers.", "index-token-p2": "Vouchers will pay for servers, to let your communities stay free and independent.", - "index-token-cta": "Learn more and get your free NFT
for 20% discount on SimpleX Token!", + "index-token-cta": "Learn more and get your free NFT
for early testing.", "index-roadmap-h2": "SimpleX Roadmap to Free Internet", "index-roadmap-2025": "2025", "index-roadmap-2025-title": "Scale to Large Communities", diff --git a/website/langs/es.json b/website/langs/es.json index 0feb776f34..9ab62eade5 100644 --- a/website/langs/es.json +++ b/website/langs/es.json @@ -6,51 +6,51 @@ "simplex-explained-tab-2-text": "2. Cómo funciona la red", "simplex-network": "Red Simplex", "simplex-explained-tab-3-text": "3. Qué ven los servidores", - "simplex-explained-tab-2-p-2": "Los servidores solo transmiten mensajes en un sentido para no disponer de la imagen completa de la conversación y conexiones del usuario.", - "simplex-explained-tab-3-p-2": "El usuario puede mejorar aún más la privacidad de sus metadatos haciendo uso de la red Tor para acceder a los servidores, evitando así la correlación por dirección IP.", + "simplex-explained-tab-2-p-2": "Los servidores sólo transmiten en un sentido para no disponer de la conversación completa o las conexiones del usuario.", + "simplex-explained-tab-3-p-2": "El usuario puede mejorar aún más la privacidad de sus metadatos, haciendo uso de la red Tor para acceder a los servidores, evitando así la correlación por dirección IP.", "smp-protocol": "Protocolo SMP", "donate": "Donación", - "copyright-label": "© 2020-2025 SimpleX | Proyecto de Código Abierto", - "simplex-chat-protocol": "Protocolo de SimpleX Chat", + "copyright-label": "© 2020-2025 SimpleX Chat | Proyecto de Código Abierto", + "simplex-chat-protocol": "Protocolo SimpleX Chat", "terms-and-privacy-policy": "Política de Privacidad", "hero-header": "Privacidad redefinida", "hero-overlay-1-textlink": "¿Por qué los ID de usuario son perjudiciales para la privacidad?", "hero-overlay-2-textlink": "¿Cómo funciona SimpleX?", - "hero-2-header": "Haga una conexión privada", + "hero-2-header": "Crea una conexión privada", "hero-overlay-1-title": "¿Cómo funciona SimpleX?", "hero-overlay-2-title": "¿Por qué los ID de usuario son perjudiciales para la privacidad?", - "feature-1-title": "Mensajes cifrados E2E con sintáxis markdown y edición", - "feature-3-title": "Grupos descentralizados cifrados E2E — sólo los usuarios conocen su existencia", - "feature-6-title": "Llamadas y videollamadas con cifrado E2E", + "feature-1-title": "Mensajes cifrados E2E
con sintáxis markdown y edición", + "feature-3-title": "Grupos descentralizados cifrados E2E, sólo los usuarios saben de su existencia", + "feature-6-title": "Llamadas y videollamadas
con cifrado E2E", "simplex-network-overlay-1-title": "Comparativa con protocolos de mensajería P2P", "developers": "Desarrolladores", "features": "Características", "blog": "Blog", "home": "Inicio", "simplex-explained-tab-3-p-1": "Para cada cola los servidores disponen de credenciales separadas y anónimas, por lo que desconocen a qué usuarios pertenecen.", - "hero-p-1": "Las demás aplicaciones usan ID de usuario: Signal, Matrix, Session, Briar, Jami, Cwtch, etc.
SimpleX no los tiene, ni siquiera números aleatorios.
Esto mejora radicalmente su privacidad.", - "hero-2-header-desc": "El vídeo muestra cómo se conecta con sus amistades a través del código QR de un solo uso, en persona o a través de videollamada. También puede conectarse compartiendo un enlace de invitación.", - "feature-7-title": "Almacenamiento portable y cifrado — podrá transferir su perfil a otro dispositivo", + "hero-p-1": "Las demás aplicaciones usan ID de usuario: Signal, Matrix, Session, Briar, Jami, Cwtch, etc.
SimpleX no los tiene, ni siquiera usa números aleatorios.
Esto mejora radicalmente tu privacidad.", + "hero-2-header-desc": "El vídeo muestra cómo te conectas con tus amistades a través del código QR de un solo uso, en persona o a través de videollamada. También puedes conectarte compartiendo un enlace de invitación.", + "feature-7-title": "Almacenamiento portable y cifrado, podrás transferir tu perfil a otro dispositivo", "simplex-private-card-4-point-2": "Para usar SimpleX a través de Tor, instala la aplicación Orbot y activa el proxy SOCKS5 (o VPN en iOS).", "simplex-private-card-3-point-1": "Para las conexiones cliente servidor se usan exclusivamente el protocolo TLS 1.2/1.3 con algoritmos robustos.", - "simplex-private-card-4-point-1": "Para proteger tu dirección IP puedes acceder a los servidores a través de la red Tor u otras redes de transporte superpuesto.", + "simplex-private-card-4-point-1": "Para proteger tu dirección IP, puedes acceder a los servidores a través Tor u otra red superpuesta de transporte.", "simplex-private-card-5-point-2": "Esto hace que mensajes de distintos tamaños parezcan iguales desde el punto de vista de servidores y observadores de red.", "simplex-private-card-6-point-1": "Muchas redes de comunicaciónes son vulnerables a ataques MITM por parte de los servidores o los proveedores de red.", "simplex-private-card-7-point-1": "Para garantizar la integridad los mensajes se numeran secuencialmente e incluyen el hash del mensaje anterior.", "simplex-private-card-7-point-2": "Si se añade, elimina o modifica algún mensaje, el destinatario es avisado.", - "simplex-private-card-8-point-1": "Los servidores SimpleX actúan como nodos de mezcla de baja latencia — los mensajes entrantes y salientes siguen un orden diferente.", - "simplex-private-card-9-point-1": "Cada cola de mensajes transmite los mensajes en un solo sentido, con las distintas direcciones de envío y recepción.", + "simplex-private-card-8-point-1": "Los servidores SimpleX actúan como nodos de mezcla de baja latencia, los mensajes entrantes y salientes siguen un orden diferente.", + "simplex-private-card-9-point-1": "Cada cola de mensajes transmite los mensajes en un sol sentido. entre las direcciónes de envío y recepción.", "simplex-explained": "SimpleX explicado", - "simplex-explained-tab-1-p-1": "Puedes crear contactos y grupos, y mantener conversaciones bidireccionales como en cualquier aplicación de mensajería.", + "simplex-explained-tab-1-p-1": "Puedes crear contactos y grupos, y mantener conversaciones bidireccionales igual que en cualquier aplicación de mensajería.", "simplex-explained-tab-1-p-2": "¿Cómo puede funcionar con colas unidireccionales y sin identificadores de usuario?", - "simplex-explained-tab-2-p-1": "Por cada conexión se usan dos colas de mensajes separadas para que el envío y recepción se hagan a través de servidores diferentes.", + "simplex-explained-tab-2-p-1": "Por cada conexión se usan dos colas de mensajes separadas, para que el envío y la recepción se realicen a través de servidores diferentes.", "chat-bot-example": "Ejemplo de bot de Chat", "chat-protocol": "Protocolo Chat", "terminal-cli": "Terminal CLI", "hero-subheader": "La primera aplicación de mensajería
sin ID de usuario", "feature-2-title": "Cifrado E2E
de imágenes, vídeos y archivos", - "feature-8-title": "Modo incógnito —
exclusivo de SimpleX Chat", - "simplex-private-1-title": "Doble capa de
cifrado de extremo a extremo", + "feature-8-title": "Modo Incógnito
exclusivo de SimpleX Chat", + "simplex-private-1-title": "Doble capa de cifrado
de extremo a extremo", "simplex-private-2-title": "Capa de cifrado
adicional en el servidor", "simplex-private-3-title": "Transporte TLS
seguro y autenticado", "simplex-private-4-title": "Acceso opcional
a través de Tor", @@ -60,86 +60,86 @@ "simplex-private-6-title": "Intercambio de claves
fuera de banda", "simplex-private-5-title": "Múltiples capas
de contenido acolchado", "simplex-private-8-title": "Mezcla de mensajes
para prevenir la correlación", - "simplex-private-10-title": "Identificadores por pares anónimos temporales", + "simplex-private-10-title": "Identificadores por pares temporales y anónimos", "simplex-private-9-title": "Colas de mensajes
unidireccionales", "simplex-private-card-1-point-1": "Protocolo de 'Double Ratchet' —
Mensajería OTR con secreto perfecto hacia adelante y recuperación de intrusión.", - "simplex-private-card-1-point-2": "NaCL cryptobox por cada cola de mensajes para prevenir la correlación de tráfico entre colas si el protocolo TLS se ve comprometido.", - "simplex-private-card-2-point-1": "Capa de cifrado adicional en la entrega desde el servidor al destinatario, para prevenir la correlación entre los tráficos de recepción y envío del servidor si el protocolo TLS se ve comprometido.", + "simplex-private-card-1-point-2": "Y si el protocolo TLS se ve comprometido, NaCL cryptobox en cada cola de mensajes para prevenir la correlación de tráfico entre las colas.", + "simplex-private-card-2-point-1": "Capa de cifrado adicional desde el servidor al destinatario, para prevenir la correlación entre el tráficos de entrada y salida del servidor si el protocolo TLS se ve comprometido.", "simplex-private-card-3-point-2": "La huella digital del servidor y la vinculación de canales evitan los ataques de respuesta y MITM.", "simplex-private-card-3-point-3": "La reanudación de la conexión está deshabilitada para evitar ataques de sesión.", "simplex-private-card-5-point-1": "SimpleX utiliza relleno de contenido en cada capa de cifrado para frustrar los ataques al tamaño de los mensajes.", "simplex-private-card-6-point-2": "Para evitarlo las aplicaciones SimpleX pasan claves de un solo uso fuera de banda cuando compartes un enlace de dirección o un código QR.", - "simplex-private-card-9-point-2": "Esto reduce los vectores de ataque y los metadatos disponibles, en comparación con los agentes de mensajes tradicionales.", - "simplex-private-card-10-point-1": "SimpleX usa direcciones y credenciales temporales anónimas por pares para cada contacto de usuario o miembro de grupo.", + "simplex-private-card-9-point-2": "Esto reduce los vectores de ataque y los metadatos disponibles en comparación con los brokers de mensajes tradicionales.", + "simplex-private-card-10-point-1": "SimpleX usa direcciones y credenciales temporales y anónimas por pares para cada contacto o miembro del grupo.", "privacy-matters-1-title": "Publicidad y discriminación en precios", "privacy-matters-1-overlay-1-title": "La privacidad revierte en ahorro", "privacy-matters-1-overlay-1-linkText": "La privacidad revierte en ahorro", "privacy-matters-2-overlay-1-title": "La privacidad le da poder", "privacy-matters-3-title": "Enjuiciamiento por asociación inocente", - "privacy-matters-3-overlay-1-linkText": "La privacidad protege su libertad", - "simplex-unique-1-title": "Su privacidad es completa", - "simplex-unique-2-title": "Está protegido
contra el spam y abuso", - "simplex-unique-3-title": "Usted controla sus datos", - "simplex-unique-4-title": "La red SimpleX le pertenece", + "privacy-matters-3-overlay-1-linkText": "La privacidad protege tu libertad", + "simplex-unique-1-title": "La privacidad es completa", + "simplex-unique-2-title": "Estás protegido
contra el spam y el abuso", + "simplex-unique-3-title": "Tu gestionas tus datos", + "simplex-unique-4-title": "La red SimpleX te pertenece", "hero-overlay-card-1-p-1": "Muchos usuarios se preguntan: si SimpleX no tiene identificadores de usuario, ¿cómo puede saber dónde entregar los mensajes?", - "hero-overlay-card-1-p-4": "Este diseño evita que se filtren metadatos de usuario a nivel de aplicación. Para mejorar aún más la privacidad y proteger su dirección IP, puede conectarse a los servidores de mensajería a través de la red Tor.", - "hero-overlay-card-1-p-6": "Podrá encontrar más información en el informe técnico SimpleX.", - "hero-overlay-card-2-p-1": "Cuando los usuarios asumen identidades persistentes, aunque sólo se trate de un número aleatorio como un identificador de sesión, existe el riesgo de que el proveedor de red o un atacante puedan observar cómo se conectan los usuarios y la cantidad de mensajes que envían.", + "hero-overlay-card-1-p-4": "Este diseño evita que se filtren metadatos de usuario a nivel de aplicación. Para mejorar aún más la privacidad y proteger tu dirección IP, puedes conectarte a los servidores de mensajería a través de la red Tor.", + "hero-overlay-card-1-p-6": "Encontrarás más información en elSimpleX whitepaper.", + "hero-overlay-card-2-p-1": "Cuando los usuarios asumen identidades persistentes, aunque sólo se trate de un número aleatorio como un identificador de sesión, existe el riesgo de que el proveedor de red o un atacante puedan observar cómo se conectan y la cantidad de mensajes que envían.", "hero-overlay-card-2-p-2": "A continuación podrían correlacionar esta información con las redes sociales públicas existentes y averiguar las identidades reales de los usuarios.", - "hero-overlay-card-2-p-3": "Incluso con las aplicaciones más privadas que utilizan los servicios de Tor v3, si habla con dos contactos a través de un mismo perfil se puede probar que estas están conectados con la misma persona.", + "hero-overlay-card-2-p-3": "Incluso con las aplicaciones más privadas que usan los servicios de Tor v3, si hablas con dos contactos a través de un mismo perfil se puede probar que estos están conectados con la misma persona.", "privacy-matters-2-overlay-1-linkText": "La privacidad te da poder", - "simplex-private-card-10-point-2": "Esto permite entregar mensajes sin identificadores de perfil de usuario y proporcionando una mayor privacidad de metadatos que las alternativas.", - "privacy-matters-3-overlay-1-title": "La privacidad protege su libertad", - "simplex-unique-1-overlay-1-title": "Privacidad total de su identidad, perfil, contactos y metadatos", + "simplex-private-card-10-point-2": "Esto permite entregar mensajes sin identificadores del perfil de usuario y proporcionar mayor privacidad de metadatos que las alternativas.", + "privacy-matters-3-overlay-1-title": "La privacidad protege tu libertad", + "simplex-unique-1-overlay-1-title": "Privacidad total de tu identidad, perfil, contactos y metadatos", "privacy-matters-2-title": "Manipulación de elecciones", "simplex-unique-2-overlay-1-title": "La mejor protección contra el spam y el abuso", "simplex-unique-3-overlay-1-title": "Titularidad, control y seguridad de tus datos", - "simplex-unique-4-overlay-1-title": "Totalmente descentralizado — los usuarios son dueños de la red SimpleX", - "hero-overlay-card-1-p-2": "Para entregar los mensajes, en lugar del identificador de usuario empleado por las demás redes, SimpleX utiliza identificadores de colas de mensajes por pares, siendo temporales, anónimos e independientes por cada conexión — no hay identificadores a largo plazo.", + "simplex-unique-4-overlay-1-title": "Totalmente descentralizado, los usuarios son dueños de la red SimpleX", + "hero-overlay-card-1-p-2": "Para entregar los mensajes, en lugar del identificador de usuario empleado por las demás redes, SimpleX utiliza identificadores de colas de mensajes por pares, siendo temporales, anónimos e independientes por cada conexión. No hay identificadores a largo plazo.", "hero-overlay-card-1-p-5": "Los perfiles de usuario, contactos y grupos sólo se almacenan en los dispositivos cliente; los mensajes se envían con cifrado de doble capa de extremo a extremo .", - "hero-overlay-card-1-p-3": "Usted define qué servidor(es) se usan para recibir los mensajes; sus contactos — los servidores que se usan para enviar los mensajes. Es probable que cada conversación use dos servidores distintos.", - "hero-overlay-card-2-p-4": "SimpleX protege frente estos ataques al no disponer de ID de usuario por diseño. Y si usa el modo incógnito, tendrá un nombre mostrado diferente por cada contacto, evitando cualquier dato compartido entre ellos.", - "simplex-network-overlay-card-1-p-1": "Los protocolos y aplicaciones de mensajería P2P presentan varios problemas que los hacen menos fiables que SimpleX, más complejos de analizar y más vulnerables a varios tipos de ataques.", + "hero-overlay-card-1-p-3": "Tu defines qué servidor(es) se usan para recibir los mensajes. Tus contactos definenen qué servidores usarás para enviar los mensajes. Es muy probable que cada conversación use dos servidores distintos.", + "hero-overlay-card-2-p-4": "SimpleX protege frente estos ataques al no disponer de ID de usuario por diseño. Y si usas el modo incógnito, tendrás un nombre diferente por cada contacto, evitando cualquier dato compartido entre ellos.", + "simplex-network-overlay-card-1-p-1": "Los protocolos y aplicaciones de mensajería P2P presentan varios problemas que los hacen menos fiables que SimpleX, más complejos de analizar y más vulnerables a ciertos tipos de ataques.", "simplex-network-overlay-card-1-li-2": "Por diseño SimpleX, a diferencia de la mayoría de las redes P2P, no tiene identificadores globales de usuario de ningún tipo, ni siquiera temporales, y sólo usa identificadores temporales por pares, lo que proporciona un mejor anonimato y protección de los metadatos.", - "simplex-network-overlay-card-1-li-3": "P2P no resuelve el problema del ataque MITM , y la mayoría de las implementaciones existentes no utilizan mensajes fuera de banda para el intercambio inicial de claves. SimpleX utiliza mensajes fuera de banda, o en algunos casos conexiones seguras y de confianza preexistentes para el intercambio inicial de claves.", - "simplex-network-overlay-card-1-li-4": "Algunos proveedores de Internet pueden bloquear las aplicaciones P2P (como BitTorrent). SimpleX es independiente del transporte — puede trabajar con protocolos web estándar, por ejemplo WebSockets.", - "privacy-matters-overlay-card-1-p-4": "La red SimpleX protege la privacidad de sus conexiones mejor que cualquier alternativa, impidiendo por completo que su gráfico social esté disponible para empresas u organizaciones. Incluso cuando se emplean los servidores configurados en SimpleX Chat, los operadores de servidores desconocen el número de usuarios o sus conexiones.", - "privacy-matters-overlay-card-2-p-1": "No hace mucho que vimos cómo las principales elecciones eran manipuladas por una reputada consultora que usaba nuestros gráficos sociales para distorsionar nuestra visión del mundo real y manipular nuestros votos.", - "privacy-matters-overlay-card-2-p-2": "Para ser objetivo y tomar decisiones independientes usted necesita tener el controlar de su espacio de información. Sólo es posible si usa una red de comunicaciónes privada que no tenga acceso a su gráfico social.", - "privacy-matters-overlay-card-2-p-3": "SimpleX es la primera red que por diseño no tiene identificadores de usuario, protegiendo así su grafico de conexiones mejor que cualquier alternativa conocida.", - "privacy-matters-overlay-card-3-p-1": "Todos deberían preocuparse por la privacidad y la seguridad de sus comunicaciones — las conversaciones inofensivas pueden ponerle en peligro incluso aunque no tenga nada que ocultar.", - "simplex-unique-overlay-card-1-p-1": "A diferencia de otras redes de mensajería, SimpleX no tiene identificadores asignados a los usuarios. No depende de números de teléfono, direcciones basadas en dominios (como es el caso del email o XMPP), nombres de usuario, claves públicas o incluso números aleatorios para identificar a sus usuarios — Los operadores de servidores SimpleX desconocen cuántas personas utilizan sus servidores.", - "simplex-unique-overlay-card-1-p-3": "Este diseño protege la privacidad de la persona que se comunica con usted ocultándole a los servidores de la red SimpleX y de cualquier observador. Para ocultar su dirección IP a los servidores puede conectarse a los servidores SimpleX mediante Tor.", - "simplex-unique-overlay-card-2-p-1": "Al no tener identificadores en la red SimpleX, nadie puede ponerse en contacto con usted salvo que comparta una dirección de usuario de un solo uso o una dirección temporal en forma de enlace o código QR.", - "simplex-unique-overlay-card-2-p-2": "Incluso con la dirección de usuario opcional, aunque puede utilizarse para enviar solicitudes de contacto spam, puedes cambiarla o eliminarla por completo sin perder ninguna de tus conexiones.", - "simplex-unique-overlay-card-3-p-2": "Los mensajes cifrados de extremo a extremo (E2E) se mantienen temporalmente en los servidores relay SimpleX hasta que se reciben, y después son borrados permanentemente.", - "simplex-network-overlay-card-1-li-1": "Para enrutar mensajes las redes P2P se basan en alguna variante de DHT. Los diseños DHT tienen que equilibrar la garantía de entrega con la latencia. SimpleX ofrece mayor garantía de entrega y menor latencia que P2P, ya que un mensaje puede transmitirse en paralelo y de forma redundante a través de varios servidores elegidos por el destinatario. En las redes P2P el mensaje se transmite secuencialmente por O(log N) nodos, usando nodos elegidos por el algoritmo.", - "simplex-network-overlay-card-1-li-5": "Todas las redes P2P conocidas pueden ser vulnerables al ataque Sybil, porque cada nodo es susceptible de ser descubierto y la red funciona como una unidad. Las medidas de mitigación conocidas requieren un componente centralizado o bien pruebas de trabajo costosas. La red SimpleX no tiene capacidad para descubrir los servidores, está fragmentada y funciona como múltiples subredes aisladas haciendo imposibles los ataques a toda la red.", - "simplex-network-overlay-card-1-li-6": "Las redes P2P pueden ser vulnerables al ataque DRDoS, donde los clientes pueden retransmitir y amplificar el tráfico, provocando una denegación de servicio en toda la red. Los clientes SimpleX sólo retransmiten el tráfico desde una conexión conocida y no pueden ser utilizados por un atacante para amplificar el tráfico en toda la red.", - "privacy-matters-overlay-card-1-p-1": "Muchas grandes empresas usan la información de con quién está conectado para calcular sus ingresos, venderle productos que realmente no necesita y determinar su precio.", + "simplex-network-overlay-card-1-li-3": "P2P no resuelve el problema del ataque MITM , y la mayoría de implementaciones existentes no usan mensajes fuera de banda para el intercambio de claves inicial. SimpleX usa mensajes fuera de banda, o en algunos casos conexiones seguras y de confianza preexistentes para el intercambio inicial de claves.", + "simplex-network-overlay-card-1-li-4": "Algunos proveedores de Internet pueden bloquear las aplicaciones P2P (como BitTorrent). SimpleX es independiente del transporte. Puede trabajar con protocolos web estándar, por ejemplo WebSockets.", + "privacy-matters-overlay-card-1-p-4": "La red SimpleX protege la privacidad de tus conexiones mejor que cualquier alternativa, impidiendo por completo que tu gráfica social esté disponible para empresas u organizaciones. Incluso cuando se emplean los servidores configurados en SimpleX Chat, los operadores de servidores desconocen el número de usuarios o sus conexiones.", + "privacy-matters-overlay-card-2-p-1": "Hace poco vimos cómo las principales elecciones eran manipuladas por una reputada compañía consultora que usaba nuestras gráficas sociales para distorsionar nuestra visión del mundo real y manipular nuestros votos.", + "privacy-matters-overlay-card-2-p-2": "Para ser objetivo y tomar decisiones independientes necesitas tener el controlar de tu espacio de información. Sólo es posible si usas una red de comunicaciónes privada que no tenga acceso a tu gráfica social.", + "privacy-matters-overlay-card-2-p-3": "SimpleX es la primera red que por diseño no tiene identificadores de usuario, protegiendo así tu gráfica de conexiones mejor que cualquier alternativa conocida.", + "privacy-matters-overlay-card-3-p-1": "Todos deberían preocuparse por la privacidad y la seguridad de sus comunicaciones. Las conversaciones inofensivas pueden ponerte en peligro incluso aunque no tengas nada que ocultar.", + "simplex-unique-overlay-card-1-p-1": "A diferencia de otras redes de mensajería, SimpleX no tiene identificadores asignados a los usuarios. No depende de números de teléfono, direcciones basadas en dominios (como es el caso del email o XMPP), nombres de usuario, claves públicas o incluso números aleatorios para identificar a sus usuarios. Los operadores de servidores SimpleX desconocen cuántas personas usan sus servidores.", + "simplex-unique-overlay-card-1-p-3": "Este diseño protege la privacidad de la persona que se comunica contigo ocultándole a los servidores de la red SimpleX y de cualquier observador. Para ocultar tu dirección IP a los servidores puedes conectarte a los servidores SimpleX mediante Tor.", + "simplex-unique-overlay-card-2-p-1": "Al no tener identificadores en la red SimpleX, nadie puede ponerse en contacto contigo salvo que compartas una dirección de usuario de un solo uso o una dirección temporal en forma de enlace o código QR.", + "simplex-unique-overlay-card-2-p-2": "Incluso con la dirección de usuario opcional, aunque pueda usarse para enviar solicitudes de contacto spam, puedes cambiarla o eliminarla por completo sin perder ninguna de tus conexiones.", + "simplex-unique-overlay-card-3-p-2": "Los mensajes cifrados de extremo a extremo (E2E) se mantienen temporalmente en los servidores SimpleX hasta que se entregan al destinatario, y después se borran definitivamente.", + "simplex-network-overlay-card-1-li-1": "Para enrutar mensajes, las redes P2P se basan en alguna variante de DHT. Los diseños DHT tienen que equilibrar la garantía de entrega y latencia. SimpleX ofrece mayor garantía de entrega y menor latencia que P2P ya que el mensaje puede transmitirse en paralelo y de forma redundante a través de varios servidores elegidos por el destinatario. En las redes P2P el mensaje se transmite secuencialmente por O(log N) nodos, usando nodos elegidos por el algoritmo.", + "simplex-network-overlay-card-1-li-5": "Todas las redes P2P conocidas pueden ser vulnerables al ataque Sybil porque cada nodo es susceptible de ser descubierto y la red funciona como una unidad. Las medidas de mitigación conocidas requieren un componente centralizado o bien costosas pruebas de trabajo. La red SimpleX no tiene la capacidad para descubrir los servidores, está fragmentada y funciona como múltiples subredes aisladas haciendo imposibles los ataques a toda la red.", + "simplex-network-overlay-card-1-li-6": "Las redes P2P pueden ser vulnerables al ataque DRDoS, en el que los clientes retransmiten y amplifican el tráfico provocando el bloqueo del servicio en la red. Los clientes SimpleX sólo transmiten el tráfico desde una conexión conocida y no pueden ser usados por un atacante para amplificar el tráfico en toda la red.", + "privacy-matters-overlay-card-1-p-1": "Las grandes empresas usan la información relativa a tus contactos para calcular tus ingresos, venderte productos que realmente no necesitas y para determinar sus precios.", "privacy-matters-overlay-card-1-p-2": "Los minoristas en línea saben que las personas con menos ingresos son más propensas a hacer compras urgentes, por lo que pueden cobrar precios más altos o eliminar descuentos.", - "privacy-matters-overlay-card-1-p-3": "Algunas compañías financieras y de seguros usan gráficos sociales para determinar primas y tipos de interés. A menudo eso genera un mayor desembolso a personas con menos ingresos — se conoce como \"prima de pobreza\".", - "privacy-matters-overlay-card-3-p-2": "Una de las historias más estremecedoras es la experiencia de Mohamedou Ould Salahi descrita en sus memorias y plasmada en la película The Mauritanian. Fue internado en la prisión militar de Guantánamo sin juicio previo, donde fue torturado durante 15 años tras una llamada telefónica a su pariente en Afganistán, bajo sospecha de estar implicado en los atentados del 11-S a pesar de que vivía en Alemania desde hacía 10 años.", - "privacy-matters-overlay-card-3-p-4": "No basta con usar mensajería cifrada de extremo a extremo, todos deberíamos utilizar las aplicaciones de mensajería que protegen la privacidad de nuestras redes personales y con quién estamos conectados.", - "privacy-matters-overlay-card-3-p-3": "Personas corrientes son detenidas por lo que comparten en Internet, incluso a través de sus cuentas \"anónimas\" e incluso en países democráticos.", - "simplex-unique-overlay-card-1-p-2": "Para enviar mensajes, SimpleX utiliza direcciones anónimas por pares de colas de mensajes unidireccionales, separadas para mensajes recibidos y enviados, y normalmente a través de servidores diferentes.", + "privacy-matters-overlay-card-1-p-3": "Algunas compañías financieras y de seguros usan las gráficas sociales para determinar primas y tipos de interés. Eso genera a menudo un mayor desembolso a personas con menores ingresos, conocido como \"prima de pobreza\".", + "privacy-matters-overlay-card-3-p-2": "Una de las historias más estremecedoras es la experiencia de Mohamedou Ould Salahi descrita en sus memorias y plasmada en la película The Mauritanian. Fue internado en la prisión militar de Guantánamo sin juicio previo, donde fue torturado durante 15 años tras una llamada telefónica a su pariente en Afganistán, bajo la sospecha de estar implicado en los atentados del 11-S a pesar de que vivía en Alemania desde hacía 10 años.", + "privacy-matters-overlay-card-3-p-4": "No basta con usar mensajería cifrada de extremo a extremo, todos deberíamos usar las aplicaciones de mensajería que protejan la privacidad de nuestras redes personales y con quién nos comunicamos.", + "privacy-matters-overlay-card-3-p-3": "Personas corrientes son detenidas por lo que comparten en Internet, incluso a través de sus cuentas \"anónimas\" e incluso en países democráticos.", + "simplex-unique-overlay-card-1-p-2": "Para la entrega de mensajes, SimpleX usa direcciones anónimas por pares de colas de mensajes unidireccionales separadas para mensajes salientes y entrantes a través de servidores diferentes.", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat almacena todos los datos de usuario únicamente en los dispositivos cliente usando un formato cifrado y portable de la base de datos, la cual puede ser exportada y transferida a cualquier dispositivo compatible.", "simplex-unique-overlay-card-3-p-3": "A diferencia de los servidores de redes federadas (correo electrónico, XMPP o Matrix), los servidores SimpleX no almacenan cuentas de usuario, sólo retransmiten mensajes, protegiendo así la privacidad de ambas partes.", - "simplex-unique-overlay-card-4-p-1": "Puede usar SimpleX con sus propios servidores y aún así comunicarse con personas que usen los servidores preconfigurados en la aplicación.", - "simplex-unique-card-1-p-1": "SimpleX protege la privacidad de su perfil, contactos y metadatos, ocultándolos de los servidores de la red SimpleX y de cualquier observador.", + "simplex-unique-overlay-card-4-p-1": "Puedes usar SimpleX con tus propios servidores y aún así comunicarte con personas que usen los servidores preconfigurados de la aplicación.", + "simplex-unique-card-1-p-1": "SimpleX protege la privacidad de tu perfil, contactos y metadatos, ocultándolos de los servidores SimpleX y de cualquier observador.", "simplex-unique-card-3-p-1": "SimpleX almacena todos los datos de usuario únicamente en los dispositivos cliente usando un formato cifrado y portable de la base de datos, la cual puede ser transferida a otro dispositivo.", - "simplex-unique-card-3-p-2": "Los mensajes cifrados de extremo a extremo (E2E) se mantienen temporalmente en los servidores relay SimpleX hasta que se reciben, y después son borrados permanentemente.", - "simplex-unique-card-4-p-2": "Puede usar SimpleX con sus propios servidores o con los servidores proporcionados por nosotros para contactar con cualquier usuario.", - "join": "Únase a", - "we-invite-you-to-join-the-conversation": "Le invitamos a unirse a la conversación", - "join-the-REDDIT-community": "Únase a la comunidad REDDIT", - "join-us-on-GitHub": "Únase a nosotros en GitHub", - "sign-up-to-receive-our-updates": "Suscríbase para recibir nuestras actualizaciones", - "donate-here-to-help-us": "Para ayudarnos haga una donación aquí", - "enter-your-email-address": "Escriba su dirección de correo electrónico", - "get-simplex": "Obtenga SimpleX desktop app", - "why-simplex-is-unique": "Por qué SimpleX es único", - "learn-more": "Descubra más", + "simplex-unique-card-3-p-2": "Los mensajes cifrados de extremo a extremo (E2E) se mantienen temporalmente en los servidores SimpleX hasta que se entregan al destinatario, y después se borran definitivamente.", + "simplex-unique-card-4-p-2": "Puedes usar SimpleX con tus propios servidores o con los servidores proporcionados por nosotros para contactar con cualquier usuario.", + "join": "Únete a", + "we-invite-you-to-join-the-conversation": "Te invitamos a unirte a la conversación", + "join-the-REDDIT-community": "Únete a la comunidad REDDIT", + "join-us-on-GitHub": "Únete a nosotros en GitHub", + "sign-up-to-receive-our-updates": "Suscríbete para recibir actualizaciones", + "donate-here-to-help-us": "Dona aquí para ayudarnos", + "enter-your-email-address": "Escribe tu correo electrónico", + "get-simplex": "Obtén SimpleX para desktop", + "why-simplex-is-unique": "Por qué SimpleX es único", + "learn-more": "Descubre más", "more-info": "Más información", "hide-info": "Ocultar información", "invitation-hero-header": "Has recibido un enlace de un solo uso para conectarte con SimpleX Chat", @@ -148,7 +148,7 @@ "install-simplex-app": "Instalar SimpleX Chat", "connect-in-app": "Conectar en la aplicación", "open-simplex-app": "Abrir la aplicación Simplex", - "tap-the-connect-button-in-the-app": "Pulse el botón ‘conectar’ en la aplicación", + "tap-the-connect-button-in-the-app": "Pulsa el botón \"conectar\" en la aplicación", "scan-the-qr-code-with-the-simplex-chat-app": "Escanear el código QR con la aplicación SimpleX Chat", "installing-simplex-chat-to-terminal": "Instalación de chat SimpleX en el terminal", "use-this-command": "Usa este comando:", @@ -158,12 +158,12 @@ "if-you-already-installed": "Si ya has instalado", "simplex-chat-for-the-terminal": "SimpleX Chat para el terminal", "copy-the-command-below-text": "copia el siguiente comando y úsalo en el chat:", - "privacy-matters-section-subheader": "Preservar la privacidad de sus metadatos — con quién habla — le protege de:", - "privacy-matters-section-label": "¡Asegúrese de que su aplicación de mensajería no puede acceder a sus datos!", + "privacy-matters-section-subheader": "Preservar la privacidad de tus metadatos, con quién te comunicas, te protege de:", + "privacy-matters-section-label": "¡Asegúrate que la mensajería no puede acceder a tus datos!", "tap-to-close": "Pulsa para cerrar", "simplex-network-1-overlay-linktext": "problemas asociados a las redes P2P", "simplex-network-3-header": "Red Simplex", - "simplex-network-3-desc": "Los servidores proporcionan colas unidireccionales para conectar a los usuarios, pero no pueden ver el mapa de conexión de la red — sólo la tienen los usuarios.", + "simplex-network-3-desc": "los servidores proporcionan colas unidireccionales para conectar a los usuarios pero no ven la gráfica de conexiónes red, sólo los usuarios pueden verla.", "comparison-section-header": "Comparación con otros protocolos", "protocol-1-text": "Signal, grandes plataformas", "protocol-2-text": "XMPP, Matrix", @@ -176,39 +176,39 @@ "yes": "Sí", "no": "No", "no-federated": "No - federado", - "comparison-section-list-point-1": "Generalmente basado en un número de teléfono, en algunos casos en nombres de usuario", + "comparison-section-list-point-1": "Generalmente basada en un número de teléfono, y en algunos casos en nombres de usuario", "comparison-section-list-point-2": "Direcciones basadas en DNS", - "comparison-section-list-point-4": "Si los servidores del operador se ven comprometidos. Verifique el código de seguridad en Signal y alguna otra aplicación para mitigarlo", + "comparison-section-list-point-4": "Si los servidores del operador se ven comprometidos. Verifica el código de seguridad en Signal y otras aplicaciónes para mitigarlo", "comparison-section-list-point-5": "No protege la privacidad de los metadatos del usuario", "comparison-section-list-point-3": "Clave pública o algun otro ID único a nivel global", - "comparison-section-list-point-6": "A pesar de que las redes P2P son distribuidas, no son federadas — funcionan como una única red", - "comparison-section-list-point-7": "Las redes P2P o bien tienen una autoridad central o toda la red puede verse comprometida", + "comparison-section-list-point-6": "A pesar de que las redes P2P son distribuidas, no son federadas. Funcionan como una única red", + "comparison-section-list-point-7": "Las redes P2P, o bien disponen de una autoridad central, o bien la red completa podría verse comprometida", "see-here": "ver aquí", - "simplex-unique-overlay-card-4-p-2": "La red SimpleX utiliza un protocolo abierto y proporciona SDK para crear chatbots, permitiendo implementar servicios con los que los usuarios pueden interactuar mediante las aplicaciones SimpleX Chat — esperamos conocer los servicios SimpleX que pueda usted crear.", - "simplex-unique-overlay-card-4-p-3": "Si está pensando en desarrollar para la red SimpleX, por ejemplo un chat bot para los usuarios de SimpleX o la integración de las librerías SimpleX Chat en sus aplicaciones móviles, por favor póngase en contacto para cualquier consejo o soporte.", - "simplex-unique-card-1-p-2": "A diferencia de cualquier otra red de mensajería existente, SimpleX no tiene identificadores asignados a los usuarios — ni siquiera números aleatorios .", - "simplex-unique-card-2-p-1": "Al no tener identificadores o dirección permanente en la red SimpleX, nadie puede ponerse en contacto con usted salvo que comparta una dirección de usuario de un solo uso o una dirección temporal en forma de enlace o código QR.", + "simplex-unique-overlay-card-4-p-2": "La red SimpleX usa un protocolo abierto y proporciona el SDK para crear chatbots, permitiendo implementar servicios con los que los usuarios interactúen con las aplicaciones SimpleX Chat — esperamos con interés ver los servicios SimpleX que creará usted.", + "simplex-unique-overlay-card-4-p-3": "Si estás considerando desarrollar para la red SimpleX, por ejemplo un chatbot para los usuarios de SimpleX o la integración de las librerías SimpleX Chat en tus aplicaciones móviles, por favor ponte en contacto para consejos y soporte.", + "simplex-unique-card-1-p-2": "A diferencia de cualquier otra red de mensajería existente, SimpleX no tiene identificadores asignados a los usuarios, ni siquiera números aleatorios.", + "simplex-unique-card-2-p-1": "Al no tener identificadores o dirección permanente en la red SimpleX, nadie puede ponerse en contacto contigo salvo que compartas una dirección de usuario de un solo uso o una dirección temporal en forma de enlace o código QR.", "simplex-unique-card-4-p-1": "La red SimpleX está completamente descentralizada y es independiente de cualquier criptodivisa o de cualquier red salvo Internet.", "contact-hero-header": "Has recibido una dirección para conectarte con SimpleX Chat", "the-instructions--source-code": "instrucciones para descargar o compilar desde el código fuente.", - "simplex-network-section-header": "Red SimpleX", + "simplex-network-section-header": "Red SimpleX", "contact-hero-subheader": "Escanéa el código QR en tu teléfono o tableta con la aplicación SimpleX Chat.", - "contact-hero-p-1": "Las claves públicas y la dirección de la cola de mensajes de este enlace NO se envían a través de la red cuando ves esta página — están contenidos en el fragmento hash del URL del enlace.", + "contact-hero-p-1": "Las claves públicas y la dirección de la cola de mensajes de este enlace NO se envían a través de la red cuando ves esta página. Están contenidos en el fragmento hash del URL del enlace.", "scan-qr-code-from-mobile-app": "Escanear el código QR desde la aplicación móvil", "to-make-a-connection": "Para establecer una conexión:", - "scan-the-qr-code-with-the-simplex-chat-app-description": "Las claves públicas y la dirección de la cola de mensajes de este enlace NO se envían a través de la red cuando ves esta página —
están contenidas en el fragmento hash del URL del enlace.", - "privacy-matters-section-header": "Por qué es importante la privacidad", - "simplex-private-section-header": "Qué hace que SimpleX sea privado", + "scan-the-qr-code-with-the-simplex-chat-app-description": "Las claves públicas y la dirección de la cola de mensajes de este enlace NO se envían a través de la red cuando ves esta página.
Están contenidos en el fragmento hash del URL del enlace.", + "privacy-matters-section-header": "Por qué es importante la privacidad", + "simplex-private-section-header": "Qué hace que SimpleX sea privado", "simplex-network-section-desc": "Simplex Chat proporciona la mejor privacidad al combinar las ventajas de P2P y las redes federadas.", - "simplex-network-1-header": "Distinto de las redes P2P", - "simplex-network-2-header": "Distinto de las redes federadas", - "simplex-network-2-desc": "Los servidores relay SimpleX NO almacenan perfiles de usuario, contactos ni mensajes entregados, NO se conectan entre sí y NO existe un directorio de servidores.", + "simplex-network-1-header": "Distinta de las redes P2P", + "simplex-network-2-header": "Distinta de las redes federadas", + "simplex-network-2-desc": "Los servidores de SimpleX NO almacenan perfiles de usuario, contactos, o mensajes entregados. NO contactan entre sí y NO existe un directorio de servidores.", "no-secure": "No - seguro", "no-resilient": "No - resiliente", "no-decentralized": "No - descentralizado", "no-private": "No - privado", - "simplex-network-1-desc": "Todos los mensajes se envían a través de los servidores, proporcionando mayor privacidad para los metadatos y una entrega asincrónica fiable de mensajes, evitando al mismo tiempo muchos de los", - "simplex-unique-overlay-card-3-p-4": "No hay identificadores ni texto cifrado en común entre el tráfico de servidor enviado y el recibido — si alguien lo está monitorizando, difícilmente podría determinar quién se comunica con quién, incluso si el protocolo TLS se ve comprometido.", + "simplex-network-1-desc": "Todos los mensajes se envían a través de los servidores, proporcionando mejor privacidad para los metadatos y entrega asíncrona fiable, evitando al mismo tiempo muchos de los", + "simplex-unique-overlay-card-3-p-4": "No hay identificadores ni texto cifrado en común entre el tráfico de servidor enviado y el recibido. Si alguien te está monitorizando, difícilmente podría determinar quién se comunica con quién, incluso si el protocolo TLS se ve comprometido.", "guide-dropdown-3": "Grupos secretos", "guide-dropdown-4": "Perfiles de chat", "guide-dropdown-5": "Gestión de datos", @@ -227,22 +227,22 @@ "docs-dropdown-6": "Servidores WebRTC", "docs-dropdown-7": "Traducir SimpleX Chat", "newer-version-of-eng-msg": "Hay una versión más reciente en inglés de esta página.", - "click-to-see": "Haga clic para ver", + "click-to-see": "Clica para ver", "menu": "Menú", "on-this-page": "En esta página", "back-to-top": "Volver arriba", "glossary": "Glosario", "stable-and-beta-versions-built-by-developers": "Versiones estables y beta compilados por los desarrolladores", - "f-droid-page-simplex-chat-repo-section-text": "Para añadirlo a tu cliente F-Droid escanea el código QR usa esta URL:", + "f-droid-page-simplex-chat-repo-section-text": "Para añadirlo al cliente F-Droid, escanea el código QR o usa esta URL:", "docs-dropdown-8": "Directorio SimpleX", "simplex-chat-repo": "Repositorio Simplex Chat", "simplex-chat-via-f-droid": "SimpleX Chat en F-Droid", "f-droid-org-repo": "Repositorio F-Droid.org", "stable-versions-built-by-f-droid-org": "Versión estable compilada por F-Droid.org", - "f-droid-page-f-droid-org-repo-section-text": "Los repositorios de SimpleX Chat y F-Droid.org firman con distinto certificado. Para cambiar, por favor exportar la base de datos y reinstala la aplicación.", - "signing-key-fingerprint": "Huella digital de la clave de firma (SHA-256)", - "releases-to-this-repo-are-done-1-2-days-later": "Las versiones aparecen varios días más tarde en este repositorio", - "comparison-section-list-point-4a": "Los servidores de retransmisión no pueden comprometer la encriptación e2e. Para evitar posibles ataques, verifique el código de seguridad mediante un canal alternativo", + "f-droid-page-f-droid-org-repo-section-text": "Los repositorios de SimpleX Chat y F-Droid.org firman con claves distintas. Para pasar de uno a otro, por favor exporta la base de datos y reinstala la aplicación.", + "signing-key-fingerprint": "Huella de la clave (SHA-256)", + "releases-to-this-repo-are-done-1-2-days-later": "Las versiones se publican varios días más tarde en este repositorio", + "comparison-section-list-point-4a": "Los servidores no pueden comprometer el cifrado E2E. Para evitar posibles ataques, verifica el código de seguridad mediante un canal alternativo", "hero-overlay-3-title": "Evaluaciones de seguridad", "hero-overlay-card-3-p-2": "Trail of Bits revisó la criptografía y los componentes de red de la plataforma SimpleX en noviembre de 2022. Saber más.", "jobs": "Únete al equipo", @@ -250,60 +250,60 @@ "hero-overlay-card-3-p-1": "Trail of Bits es una consultora de seguridad y tecnología líder cuyos clientes incluyen grandes tecnológicas, agencias gubernamentales e importantes proyectos de blockchain.", "docs-dropdown-9": "Descargas", "please-enable-javascript": "Habilita JavaScript para ver el código QR.", - "please-use-link-in-mobile-app": "Usa el enlace en la apliación móvil", + "please-use-link-in-mobile-app": "Por favor, usa el enlace en la apliación móvil", "docs-dropdown-10": "Transparencia", "docs-dropdown-11": "FAQ", "docs-dropdown-12": "Seguridad", "docs-dropdown-14": "SimpleX para empresas", "hero-overlay-card-3-p-3": "Trail of Bits ha revisado el diseño criptográfico de los protocolos de red SimpleX en julio del 2024. Saber más.", "directory": "Directorio", - "about-and-contact-us": "Acerca de & Contacta con nosotros", - "index-hero-h1": "Sea
Libre", + "about-and-contact-us": "Quiénes somos & Contacto", + "index-hero-h1": "Sé
Libre", "index-hero-h2": "Libertad y Seguridad
en Tus Comunicaciones", - "index-hero-p1": "La primera red donde su identidad, contactos y grupos le pertenecen.", + "index-hero-p1": "La primera red donde tu identidad, contactos y grupos te pertenecen.", "index-hero-download-desktop-btn-title": "Descargar SimpleX Desktop App", - "index-testflight-title": "Vista previa pública de iOS en TestFlight", - "index-f-droid-title": "Repositorio SimpleX F-Droid", - "index-security-assessment-title": "Evaluación de seguridad", - "index-security-review-2022-title": "Revisión de Seguridad 2022", - "index-security-review-2024-title": "Revisión de Seguridad 2024", - "index-security-audits-label": "Auditorías de
Seguridad", - "index-publications-privacy-guides-title": "Recomendaciones de mensajería", + "index-testflight-title": "Betas SimpleX para iOS en TestFlight", + "index-f-droid-title": "SimpleX app vía F-Droid", + "index-security-assessment-title": "Auditorías de seguridad", + "index-security-review-2022-title": "Auditoría de Seguridad 2022", + "index-security-review-2024-title": "Auditoría de Seguridad 2024", + "index-security-audits-label": "Auditorías
en Seguridad", + "index-publications-privacy-guides-title": "Recomendaciones de mensajería de Privacy Guides", "index-publications-whonix-title": "Recomendación de mensajería de Whonix", - "index-publications-heise-title": "Publicación", - "index-publications-kuketz-title": "Revisión", - "index-publications-optout-title": "Entrevista en podcast", - "worlds-most-secure-messaging": "La Mensajería Más Segura del Mundo", - "index-messaging-p1": "Le mensajería SimpleX dispone de cifrado de extremo a extremo de vanguardia.", - "index-messaging-p2": "Por su seguridad y privacidad los servidores no pueden ver sus mensajes ni con quién habla.", - "index-messaging-cta": "Sepa más sobre la mensajería SimpleX", - "index-nextweb-h2": "Usted Posee
La Web Futura", - "index-nextweb-p1": "SimpleX se funda en la creencia de que usted debe ser propietario de su identidad, contactos y comunidades.", - "index-nextweb-p2": "Una red abierta y descentralizada que le permite conectarse con personas y compartir ideas: libre y segura.", - "index-token-h2": "Comunidades Que Perduran", - "index-token-p1": "Usted apoyará a sus grupos favoritos con futuros Vales Comunitarios.", - "index-token-p2": "Los vales pagarán los servidores para que sus comunidades sigan siendo libres e independientes.", - "index-token-cta": "Conoce más y obten tu NFT gratuito
para un 20% de descuento en SimpleX Token!", - "index-roadmap-h2": "Hoja de ruta SimpleX hacia el Internet Libre", + "index-publications-heise-title": "Publicaciones Heise Online", + "index-publications-kuketz-title": "Revisión por Mike Kuketz", + "index-publications-optout-title": "Entrevista en podcast de OptOut", + "worlds-most-secure-messaging": "Mensajería Más Segura Del Mundo", + "index-messaging-p1": "La mensajería SimpleX posee un cifrado de extremo a extremo de vanguardia.", + "index-messaging-p2": "Para tu seguridad y privacidad los servidores no pueden ver tus mensajes ni con quién te comunicas.", + "index-messaging-cta": "Descubre más sobre la mensajería SimpleX", + "index-nextweb-h2": "La Web
Del Futuro
Te Pertenece", + "index-nextweb-p1": "SimpleX se funda en la creencia de que debes ser el propietario de tu identidad, contactos y comunidades.", + "index-nextweb-p2": "Una red abierta y descentralizada que te permite conectarte con personas y compartir ideas: libre y segura.", + "index-token-h2": "Comunidades Duraderas", + "index-token-p1": "Podrás apoyar a tus grupos favoritos con los futuros Vales Comunitarios.", + "index-token-p2": "Los vales costearán los servidores para que tus comunidades sigan siendo libres e independientes.", + "index-token-cta": "Descubre más y obtén tu NFT gratuito
por participar en las pruebas.", + "index-roadmap-h2": "Ruta SimpleX hacía el Internet Libre", "index-roadmap-2025": "2025", - "index-roadmap-2025-title": "Escalar a Grandes Comunidades", - "index-roadmap-2025-desc": "Huyendo de plataformas centralizadas", + "index-roadmap-2025-title": "Escalar a Comunidades Grandes", + "index-roadmap-2025-desc": "Huir de plataformas centralizadas", "index-roadmap-2026": "2026", "index-roadmap-2026-title": "Comunidades y Servidores Sostenibles", - "index-roadmap-2026-desc": "Lanzamiento de los Vales Comunitarios", + "index-roadmap-2026-desc": "Lanzamiento de Vales Comunitarios", "index-roadmap-2027": "2027", - "index-roadmap-2027-title": "Haga Crecer Sus Comunidades", - "index-roadmap-2027-desc": "Herramientas para promocionar sus comunidades", - "index-directory-h2": "Únase a las Comunidades SimpleX", - "index-directory-p1": "Cientos de miles de personas ya confían en la mensajería SimpleX.", - "index-directory-p2": "¡Encuentre sus comunidades en el directorio SimpleX y cree la suya!", - "index-directory-cta": "Ver el Directorio SimpleX", + "index-roadmap-2027-title": "Haz Crecer Tus Comunidades", + "index-roadmap-2027-desc": "Herramientas para la promoción", + "index-directory-h2": "Únete a las Comunidades SimpleX", + "index-directory-p1": "Miles de personas confían ya en la mensajería SimpleX.", + "index-directory-p2": "¡Encuentra comunidades en el directorio SimpleX y crea la tuya!", + "index-directory-cta": "Ir al Directorio SimpleX", "index-directory-users-group-title": "SimpleX users group", - "how-secure-comparison-title": "¿Qué nivel de seguridad ofrece el cifrado de extremo a extremo en los distintos servicios de mensajería?", + "how-secure-comparison-title": "Comparativa del cifrado de extremo a extremo en los distintos mensajeros", "how-secure-message-padding": "Relleno de mensajes", "how-secure-repudiation-deniability": "Repudio (negabilidad)", "how-secure-forward-secrecy": "Secreto hacia adelante", - "how-secure-break-in-recovery": "Seguridad posterior a ser comprometido", + "how-secure-break-in-recovery": "Seguridad tras ser comprometido", "how-secure-two-factor-key-exchange": "Intercambio de claves de dobre factor", "how-secure-post-quantum-hybrid-crypto": "Cifrado híbrido postcuántico", "messengers-comparison-section-list-point-1": "Briar rellena los mensajes hasta el tamaño redondeado a 1024 bytes, Signal a 160 bytes", @@ -311,5 +311,5 @@ "messengers-comparison-section-list-point-3": "Parece que el uso de firmas criptográficas compromete el repudio (negabilidad) pero es necesario aclararlo.", "messengers-comparison-section-list-point-4": "La implementación multi‑dispositivo compromete la seguridad posterior a ser comprometida del Double Ratchet", "messengers-comparison-section-list-point-5": "El intercambio de clave de doble factor es opcional mediante la verificación del código de seguridad.", - "messengers-comparison-section-list-point-6": "El acuerdo de claves postcuántico es \"parcial\" — solo protege determinados pasos del ratchet." + "messengers-comparison-section-list-point-6": "El acuerdo de claves postcuántico es \"parcial\", solo protege determinados pasos del ratchet." } diff --git a/website/langs/fi.json b/website/langs/fi.json index 460aa7989b..d30c465222 100644 --- a/website/langs/fi.json +++ b/website/langs/fi.json @@ -112,7 +112,7 @@ "simplex-explained-tab-1-p-1": "Voit luoda yhteyshenkilöitä ja ryhmiä sekä käydä kaksisuuntaisia keskusteluja kuten missä tahansa muussa viestisovelluksessa.", "simplex-explained-tab-3-p-1": "Palvelimilla on erilliset anonyymit tunnistetiedot kullekin jonolle, eivätkä ne tiedä, mille käyttäjille ne kuuluvat.", "donate": "Lahjoita", - "copyright-label": "© 2020-2025 SimpleX | Avoin projekti", + "copyright-label": "© 2020-2025 SimpleX Chat | Avoin projekti", "hero-p-1": "Muissa sovelluksissa on käyttäjätunnuksia: Signal, Matrix, Session, Briar, Jami, Cwtch, jne.
SimpleX ei käytä niitä, ei edes satunnaisia numeroita.
Tämä parantaa yksityisyyttäsi radikaalisti.", "simplex-private-1-title": "2 kerrosta päästä päähän salattua viestintää", "simplex-private-2-title": "Lisäkerros palvelimen salaukselle", diff --git a/website/langs/fr.json b/website/langs/fr.json index b3613bc3da..8ff71af8dc 100644 --- a/website/langs/fr.json +++ b/website/langs/fr.json @@ -21,7 +21,7 @@ "smp-protocol": "Protocole SMP", "chat-protocol": "Protocole de chat", "donate": "Faire un don", - "copyright-label": "© 2020-2025 SimpleX | Projet Open-Source", + "copyright-label": "© 2020-2025 SimpleX Chat | Projet Open-Source", "simplex-chat-protocol": "Protocole SimpleX Chat", "terminal-cli": "Terminal CLI", "terms-and-privacy-policy": "Politique de confidentialité", diff --git a/website/langs/he.json b/website/langs/he.json index 9673bea871..03dc671919 100644 --- a/website/langs/he.json +++ b/website/langs/he.json @@ -53,7 +53,7 @@ "smp-protocol": "פרוטוקול SMP", "chat-protocol": "פרוטוקול צ'אט", "donate": "תרומה", - "copyright-label": "© 2020-2025 SimpleX | פרויקט קוד פתוח", + "copyright-label": "© 2020-2025 SimpleX Chat | פרויקט קוד פתוח", "hero-p-1": "לאפליקציות אחרות יש מזהי משתמש: Signal, Matrix, Session, Briar, Jami, Cwtch וכו'.
ל-SimpleX אין, אפילו לא מספרים אקראיים.
זה משפר באופן קיצוני את הפרטיות שלך.", "hero-overlay-2-title": "מדוע מזהי משתמש מזיקים לפרטיות?", "feature-6-title": "שיחות שמע ווידאו
מוצפנות מקצה לקצה", diff --git a/website/langs/hu.json b/website/langs/hu.json index 6590f11cc2..85876c8b92 100644 --- a/website/langs/hu.json +++ b/website/langs/hu.json @@ -11,16 +11,16 @@ "simplex-explained-tab-1-text": "1. Felhasználói élmény", "simplex-explained-tab-2-text": "2. Hogyan működik", "simplex-explained-tab-3-text": "3. Mit látnak a kiszolgálók", - "simplex-explained-tab-1-p-1": "Létrehozhat kapcsolatokat és csoportokat, valamint kétirányú beszélgetéseket folytathat, ugyanúgy mint bármely más üzenetváltó-alkalmazásban.", - "simplex-explained-tab-1-p-2": "Hogyan működhet egyirányú üzenetvárólistával és felhasználói profilazonosítók nélkül?", + "simplex-explained-tab-1-p-1": "Csoportokat hozhat létre, valamint kétirányú beszélgetéseket folytathat a partnereivel, ugyanúgy mint bármely más üzenetváltó-alkalmazásban.", + "simplex-explained-tab-1-p-2": "Hogyan működhet egyirányú várólistával és felhasználói profilazonosítók nélkül?", "simplex-explained-tab-2-p-1": "Minden kapcsolathoz két különböző üzenetküldési várólistát használ a különböző kiszolgálókon keresztül történő üzenetküldéshez és -fogadáshoz.", "simplex-explained-tab-2-p-2": "A kiszolgálók csak egyetlen irányba továbbítják az üzeneteket, anélkül, hogy teljes képet kapnának a felhasználók beszélgetéseiről vagy kapcsolatairól.", - "simplex-explained-tab-3-p-1": "A kiszolgálók minden egyes üzenetvárólistához külön névtelen hitelesítő-adatokkal rendelkeznek, és nem tudják, hogy melyik felhasználóhoz tartoznak.", - "simplex-explained-tab-3-p-2": "A felhasználók tovább fokozhatják a metaadatok adatvédelmét, ha a Tor hálózat használatával férnek hozzá a kiszolgálókhoz, így megakadályozva az IP-cím szerinti korrelációt.", + "simplex-explained-tab-3-p-1": "A kiszolgálók minden egyes várólistához külön névtelen hitelesítő-adatokkal rendelkeznek, és nem tudják, hogy melyik felhasználóhoz tartoznak.", + "simplex-explained-tab-3-p-2": "A felhasználók tovább fokozhatják a metaadatok védelmét, ha a Tor hálózat használatával férnek hozzá a kiszolgálókhoz, így megakadályozva az IP-cím szerinti korrelációt.", "smp-protocol": "SMP-protokoll", "chat-protocol": "Csevegési protokoll", "donate": "Adományozás", - "copyright-label": "© 2020-2025 SimpleX | Nyílt forráskódú projekt", + "copyright-label": "© 2020-2025 SimpleX Chat | Nyílt forráskódú projekt", "simplex-chat-protocol": "A SimpleX Chat protokoll", "terminal-cli": "Terminál CLI", "terms-and-privacy-policy": "Adatvédelmi irányelvek", @@ -41,7 +41,7 @@ "feature-4-title": "Végpontok között titkosított hangüzenetek", "feature-5-title": "Eltűnő üzenetek", "feature-6-title": "Végpontok között titkosított
hang- és videóhívások", - "feature-7-title": "Hordozható titkosított alkalmazás-adattárolás — profil átköltöztetése egy másik eszközre", + "feature-7-title": "Hordozható, titkosított alkalmazás-adattárolás — profil átköltöztetése egy másik eszközre", "feature-8-title": "Az inkognitómód —
egyedülálló a SimpleX Chatben", "simplex-network-overlay-1-title": "Összehasonlítás más P2P-üzenetküldő protokollokkal", "simplex-private-1-title": "2 rétegű végpontok közötti titkosítás", @@ -51,82 +51,82 @@ "simplex-private-6-title": "Sávon kívüli
kulcscsere", "simplex-private-7-title": "Üzenetintegritás
hitelesítés", "simplex-private-8-title": "Üzenetek keverése
a korreláció csökkentése érdekében", - "simplex-private-9-title": "Egyirányú
üzenetvárólista", + "simplex-private-9-title": "Egyirányú
várólista az üzenetekhez", "simplex-private-10-title": "Ideiglenes, névtelen, páronkénti azonosítók", - "simplex-private-card-1-point-1": "Double-Ratchet-protokoll —
OTR-üzenetküldés, sérülés utáni titkosság-védelemmel és -helyreállítással.", - "simplex-private-card-1-point-2": "NaCL cryptobox minden egyes üzenetvárólistához, hogy megakadályozza a forgalom korrelációját az üzenetvárólisták között, ha a TLS veszélybe kerül.", + "simplex-private-card-1-point-1": "Dupla racsnis protokoll —
OTR-üzenetküldés, kompromittálás előtti és utáni titkosság-védelemmel.", + "simplex-private-card-1-point-2": "NaCL cryptobox minden egyes várólistához, hogy megakadályozza a forgalom korrelációját az üzenetek várólistái között, ha a TLS veszélybe kerül.", "simplex-private-card-2-point-1": "Kiegészítő kiszolgáló titkosítási réteg a címzettnek történő kézbesítéshez, hogy megakadályozza a fogadott és az elküldött kiszolgálóforgalom közötti korrelációt, ha a TLS veszélybe kerül.", "simplex-private-card-3-point-1": "A kliens és a kiszolgálók közötti kapcsolatokhoz csak az erős algoritmusokkal rendelkező TLS 1.2/1.3 protokollt használja.", "simplex-private-card-3-point-2": "A kiszolgáló ujjlenyomata és a csatornakötés megakadályozza a MITM- és a visszajátszási támadásokat.", "simplex-private-card-3-point-3": "Az újrakapcsolódás le van tiltva a munkamenet elleni támadások megelőzése érdekében.", "simplex-private-card-4-point-1": "Az IP-címe védelme érdekében a kiszolgálókat a Tor hálózaton vagy más átvitelátfedő hálózaton keresztül is elérheti.", "simplex-private-card-6-point-1": "Számos kommunikációs hálózat sebezhető a kiszolgálók vagy a hálózat-szolgáltatók MITM-támadásaival szemben.", - "simplex-private-card-6-point-2": "Ennek megakadályozása érdekében a SimpleX-alkalmazások egyszeri kulcsokat adnak át sávon kívül, amikor egy címet hivatkozásként vagy QR-kódként oszt meg.", + "simplex-private-card-6-point-2": "Ennek megakadályozása érdekében a SimpleX alkalmazások egyszeri kulcsokat adnak át sávon kívül, amikor egy címet hivatkozásként vagy QR-kódként oszt meg.", "simplex-private-card-7-point-1": "Az integritás garantálása érdekében az üzenetek sorszámozással vannak ellátva, és tartalmazzák az előző üzenet kivonatát.", "simplex-private-card-7-point-2": "Ha bármilyen üzenetet hozzáadnak, eltávolítanak vagy módosítanak, a címzett értesítést kap róla.", "simplex-private-card-8-point-1": "A SimpleX kiszolgálók alacsony késleltetésű keverési csomópontokként működnek — a bejövő és kimenő üzenetek sorrendje eltérő.", - "simplex-private-card-9-point-1": "Minden üzenetvárólista egy irányba továbbítja az üzeneteket, a különböző küldési és vételi címekkel.", + "simplex-private-card-9-point-1": "Minden várólista az üzenetekhez egy irányba továbbítja az üzeneteket, a különböző küldési és fogadási címeken.", "simplex-private-card-9-point-2": "Kevesebb támadási felülettel rendelkezik, mint a hagyományos üzenetváltó-alkalmazások, és kevesebb metaadatot tesz elérhetővé.", "simplex-private-card-10-point-1": "A SimpleX ideiglenes, névtelen, páros címeket és hitelesítő adatokat használ minden egyes felhasználói kapcsolathoz vagy csoporttaghoz.", "simplex-private-card-10-point-2": "Lehetővé teszi az üzenetek felhasználói profilazonosítók nélküli kézbesítését, ami az alternatíváknál jobb metaadat-védelmet biztosít.", "privacy-matters-1-overlay-1-title": "Az adatvédelemmel pénzt spórol meg", "privacy-matters-1-overlay-1-linkText": "Az adatvédelemmel pénzt spórol meg", - "privacy-matters-2-title": "A választások manipulálása", + "privacy-matters-2-title": "Választások manipulálása", "privacy-matters-2-overlay-1-title": "Az adatvédelem hatalmat ad", "privacy-matters-2-overlay-1-linkText": "Az adatvédelem hatalmat ad", "privacy-matters-3-title": "Ártatlan összefüggés miatti vádemelés", "privacy-matters-3-overlay-1-title": "Az adatvédelem szabaddá tesz", "privacy-matters-3-overlay-1-linkText": "Az adatvédelem szabaddá tesz", "simplex-unique-1-title": "Teljes magánéletet élvezhet", - "simplex-unique-1-overlay-1-title": "Személyazonosságának, profiljának, kapcsolatainak és metaadatainak teljes körű védelme", - "simplex-unique-2-title": "Véd
a kéretlen tartalmaktól és a visszaélésektől", + "simplex-unique-1-overlay-1-title": "Személyazonosságának, profiljának, partnereinek és a metaadatok teljes körű védelme", + "simplex-unique-2-title": "Véd a kéretlen tartalmaktól
és a visszaélésektől", "simplex-unique-2-overlay-1-title": "A legjobb védelem a kéretlen tartalmak és a visszaélések ellen", - "simplex-unique-3-title": "Ön kezeli az adatait", + "simplex-unique-3-title": "Ön kezeli a saját adatait", "simplex-unique-3-overlay-1-title": "Az adatok biztonsága és kezelése az Ön kezében van", "simplex-unique-4-title": "Öné a SimpleX hálózat", "simplex-unique-4-overlay-1-title": "Teljesen decentralizált — a SimpleX hálózat a felhasználóké", "hero-overlay-card-1-p-1": "Sok felhasználó kérdezte: ha a SimpleXnek nincsenek felhasználói azonosítói, honnan tudja, hogy hová kell eljuttatni az üzeneteket?", "hero-overlay-card-1-p-2": "Az üzenetek kézbesítéséhez az összes többi hálózat által használt felhasználói azonosítók helyett a SimpleX az üzenetek várólistába rendezéséhez ideiglenes, névtelen, páros azonosítókat használ, külön-külön minden egyes kapcsolathoz — nincsenek hosszú távú azonosítók.", "hero-overlay-card-1-p-4": "Ez a kialakítás megakadályozza a felhasználók metaadatainak kiszivárgását az alkalmazás szintjén. Az adatvédelem további javítása és az IP-cím védelme érdekében az üzenetküldő kiszolgálókhoz Tor hálózaton keresztül is kapcsolódhat.", - "hero-overlay-card-1-p-5": "Csak a kliensek tárolják a felhasználói profilokat, kapcsolatokat és csoportokat; az üzenetek küldése 2 rétegű végpontok közötti titkosítással történik.", - "hero-overlay-card-1-p-6": "További leírást a SimpleX ismertetőben olvashat.", + "hero-overlay-card-1-p-5": "Csak a kliensek tárolják a felhasználói profilokat, partnereket és csoportokat; az üzenetek küldése 2 rétegű végpontok közötti titkosítással történik.", + "hero-overlay-card-1-p-6": "További leírást a SimpleX ismertetőben olvashat.", "hero-overlay-card-2-p-1": "Ha a felhasználók állandó azonosítóval rendelkeznek, még akkor is, ha ez csak egy véletlenszerű szám, például egy munkamenet-azonosító, fennáll annak a veszélye, hogy a szolgáltató vagy egy támadó megfigyelheti, azt hogy hogyan kapcsolódnak a felhasználók egymáshoz, és hány üzenetet küldenek egymásnak.", "hero-overlay-card-2-p-2": "Ezt az információt aztán összefüggésbe hozhatják a meglévő nyilvános közösségi hálózatokkal, és meghatározhatnak néhány valódi személyazonosságot.", - "hero-overlay-card-2-p-3": "Még a Tor v3 szolgáltatásokat használó, legprivátabb alkalmazások esetében is, ha két különböző kapcsolattartóval beszél ugyanazon a profilon keresztül, bizonyítani tudják, hogy ugyanahhoz a személyhez kapcsolódnak.", + "hero-overlay-card-2-p-3": "Még a Tor v3 szolgáltatásokat használó, legprivátabb alkalmazások esetében is, ha két különböző partnerével beszél ugyanazon a profilon keresztül, bizonyítani tudják, hogy ugyanahhoz a személyhez kapcsolódnak.", "hero-overlay-card-2-p-4": "A SimpleX úgy védekezik ezen támadások ellen, hogy nem tartalmaz felhasználói azonosítókat. Ha pedig használja az inkognitómódot, akkor minden egyes létrejött kapcsolatban más-más felhasználó név jelenik meg, így elkerülhető a közöttük lévő összefüggések teljes bizonyítása.", "hero-overlay-card-3-p-1": "Trail of Bits egy vezető biztonsági és technológiai tanácsadó cég, amelynek az ügyfelei közé tartoznak nagy technológiai cégek, kormányzati ügynökségek és jelentős blokklánc projektek.", "hero-overlay-card-3-p-2": "A Trail of Bits 2022 novemberében auditálta a SimpleX protokollok kriptográfiai és hálózati komponenseit. További információk.", - "simplex-network-overlay-card-1-li-1": "A P2P-hálózatok az üzenetek továbbítására a DHT valamelyik változatát használják. A DHT kialakításakor egyensúlyt kell teremteni a kézbesítési garancia és a késleltetés között. A SimpleX jobb kézbesítési garanciával és alacsonyabb késleltetéssel rendelkezik, mint a P2P, mivel az üzenet redundánsan, a címzett által kiválasztott kiszolgálók segítségével több kiszolgálón keresztül párhuzamosan továbbítható. A P2P-hálózatokban az üzenet O(log N) csomóponton halad át szekvenciálisan, az algoritmus által kiválasztott csomópontok segítségével.", - "simplex-network-overlay-card-1-li-2": "A SimpleX kialakítása a legtöbb P2P-hálózattól eltérően nem rendelkezik semmiféle globális felhasználói azonosítóval, még ideiglenessel sem, és csak az üzenetekhez használ ideiglenes, páros azonosítókat, ami jobb névtelenséget és metaadatvédelmet biztosít.", - "simplex-network-overlay-card-1-li-3": "A P2P nem oldja meg a MITM-támadás problémát, és a legtöbb létező implementáció nem használ sávon kívüli üzeneteket a kezdeti kulcscseréhez. A SimpleX a kezdeti kulcscseréhez sávon kívüli üzeneteket, vagy bizonyos esetekben már meglévő biztonságos és megbízható kapcsolatokat használ.", - "simplex-network-overlay-card-1-li-6": "A P2P-hálózatok sebezhetőek lehetnek a DRDoS-támadással szemben, amikor a kliensek képesek a forgalmat újraközvetíteni és felerősíteni, ami az egész hálózatra kiterjedő szolgáltatásmegtagadást eredményez. A SimpleX kliensek csak az ismert kapcsolatból származó forgalmat továbbítják, és a támadó nem használhatja őket arra, hogy az egész hálózatban felerősítse a forgalmat.", - "simplex-network-overlay-card-1-li-5": "Minden ismert P2P-hálózat sebezhető Sybil támadással, mert minden egyes csomópont felderíthető, és a hálózat egészként működik. A támadások enyhítésére szolgáló ismert intézkedés lehet egy központi kiszolgáló (pl.: tracker), vagy egy drága tanúsítvány. A SimpleX hálózat nem ismeri fel a kiszolgálókat, töredezett és több elszigetelt alhálózatként működik, ami lehetetlenné teszi az egész hálózatra kiterjedő támadásokat.", + "simplex-network-overlay-card-1-li-1": "A P2P-hálózatok az üzenetek továbbítására a DHT valamelyik változatát használják. A DHT kialakításakor egyensúlyt kell teremteni a kézbesítési garancia és a késleltetés között. A SimpleX jobb kézbesítési garanciával és alacsonyabb késleltetéssel rendelkezik, mint a P2P, mivel az üzenet redundánsan, a címzett által kiválasztott kiszolgálók segítségével több kiszolgálón keresztül párhuzamosan továbbítható. A P2P-hálózatokban az üzenet O(log N) csomóponton halad át szekvenciálisan, az algoritmus által kiválasztott csomópontok segítségével.", + "simplex-network-overlay-card-1-li-2": "A SimpleX kialakítása a legtöbb P2P-hálózattól eltérően nem rendelkezik semmiféle globális felhasználói azonosítóval, még ideiglenessel sem, és csak az üzenetekhez használ ideiglenes, páros azonosítókat, ami jobb névtelenséget és metaadat-védelmet biztosít.", + "simplex-network-overlay-card-1-li-3": "A P2P nem oldja meg a MITM-támadás problémát, és a legtöbb létező implementáció nem használ sávon kívüli üzeneteket a kezdeti kulcscseréhez. A SimpleX a kezdeti kulcscseréhez sávon kívüli üzeneteket, vagy bizonyos esetekben már meglévő biztonságos és megbízható kapcsolatokat használ.", + "simplex-network-overlay-card-1-li-6": "A P2P-hálózatok sebezhetőek lehetnek a DRDoS-támadással szemben, amikor a kliensek képesek a forgalmat újraközvetíteni és felerősíteni, ami az egész hálózatra kiterjedő szolgáltatásmegtagadást eredményez. A SimpleX kliensek csak az ismert kapcsolatból származó forgalmat továbbítják, és a támadó nem használhatja őket arra, hogy az egész hálózatban felerősítse a forgalmat.", + "simplex-network-overlay-card-1-li-5": "Minden ismert P2P-hálózat sebezhető Sybil támadással, mert minden egyes csomópont felderíthető, és a hálózat egészként működik. A támadások enyhítésére szolgáló ismert intézkedés lehet egy központi kiszolgáló (például: tracker), vagy egy drága tanúsítvány. A SimpleX hálózat nem ismeri fel a kiszolgálókat, töredezett és több elszigetelt alhálózatként működik, ami lehetetlenné teszi az egész hálózatra kiterjedő támadásokat.", "privacy-matters-overlay-card-1-p-1": "Sok nagyvállalat arra használja fel az önnel kapcsolatban álló személyek adatait, hogy megbecsülje az ön jövedelmét, hogy olyan termékeket adjon el önnek, amelyekre valójában nincs is szüksége, és hogy meghatározza az árakat.", "privacy-matters-overlay-card-1-p-2": "Az online kiskereskedők tudják, hogy az alacsonyabb jövedelműek nagyobb valószínűséggel vásárolnak azonnal, ezért magasabb árakat számíthatnak fel, vagy eltörölhetik a kedvezményeket.", - "privacy-matters-overlay-card-1-p-3": "Egyes pénzügyi és biztosítótársaságok szociális grafikonokat használnak a kamatlábak és a díjak meghatározásához. Ez gyakran arra készteti az alacsonyabb jövedelmű embereket, hogy többet fizessenek — ez az úgynevezett „szegénységi prémium”.", + "privacy-matters-overlay-card-1-p-3": "Egyes pénzügyi és biztosítótársaságok szociális grafikonokat használnak a kamatlábak és a díjak meghatározásához. Ez gyakran arra készteti az alacsonyabb jövedelmű embereket, hogy többet fizessenek — ez az úgynevezett „szegénységi prémium”.", "privacy-matters-overlay-card-1-p-4": "A SimpleX hálózat minden alternatívánál jobban védi a kapcsolatainak adatait, teljes mértékben megakadályozva, hogy az ismeretségi-hálója bármilyen vállalat vagy szervezet számára elérhetővé váljon. Még ha az emberek a SimpleX Chat által előre beállított kiszolgálókat is használják, sem az alkalmazások, sem a kiszolgálók üzemeltetői nem ismerik, sem felhasználók számát, sem a kapcsolataikat.", - "privacy-matters-overlay-card-2-p-1": "Nem is olyan régen megfigyelhettük, hogy a nagy választásokat manipulálta egy neves tanácsadó cég, amely az ismeretségi-háló segítségével eltorzította a valós világról alkotott képünket, és manipulálta a szavazatainkat.", + "privacy-matters-overlay-card-2-p-1": "Nem is olyan régen megfigyelhettük, hogy a nagy választásokat manipulálta egy neves tanácsadó cég, amely az ismeretségi-háló segítségével eltorzította a valós világról alkotott képünket, és manipulálta a szavazatainkat.", "privacy-matters-overlay-card-2-p-2": "Ahhoz, hogy objektív legyen és független döntéseket tudjon hozni, az információs terét is kézben kell tartania. Ez csak akkor lehetséges, ha privát kommunikációs hálózatot használ, amely nem fér hozzá az ismeretségi-hálójához.", "privacy-matters-overlay-card-2-p-3": "A SimpleX az első olyan hálózat, amely eleve nem rendelkezik felhasználói azonosítókkal, így jobban védi az ismeretségi-hálóját, mint bármely ismert alternatíva.", "privacy-matters-overlay-card-3-p-1": "Mindenkinek törődnie kell a magánélet és a kommunikáció biztonságával — az ártalmatlan beszélgetések veszélybe sodorhatják, még akkor is, ha nincs semmi rejtegetnivalója.", - "privacy-matters-overlay-card-3-p-2": "Az egyik legmegdöbbentőbb a Mohamedou Ould Salahi memoárjában leírt és az „A mauritániai” c. filmben bemutatott történet. Őt bírósági tárgyalás nélkül a guantánamói táborba zárták, és ott kínozták 15 éven át, miután egy afganisztáni rokonát telefonon felhívta, akit azzal gyanúsítottak a hatóságok, hogy köze van a 9/11-es merényletekhez, holott Salahi az előző 10 évben Németországban élt.", - "privacy-matters-overlay-card-3-p-3": "Átlagos embereket letartóztatnak azért, amit online megosztanak, még „névtelen” fiókjaikon keresztül is, még demokratikus országokban is.", + "privacy-matters-overlay-card-3-p-2": "Az egyik legmegdöbbentőbb a Mohamedou Ould Salahi memoárjában leírt és az „A mauritániai” c. filmben bemutatott történet. Őt bírósági tárgyalás nélkül a guantánamói táborba zárták, és ott kínozták 15 éven át, miután egy afganisztáni rokonát telefonon felhívta, akit azzal gyanúsítottak a hatóságok, hogy köze van a 9/11-es merényletekhez, holott Salahi az előző 10 évben Németországban élt.", + "privacy-matters-overlay-card-3-p-3": "Átlagos embereket letartóztatnak azért, amit online megosztanak, még „névtelen” fiókjaikon keresztül is, még demokratikus országokban is.", "privacy-matters-overlay-card-3-p-4": "Nem elég csak egy végpontok között titkosított üzenetváltó-alkalmazást használnunk, mindannyiunknak olyan üzenetváltó-alkalmazásokat kell használnunk, amelyek védik a személyes partnereink magánéletét — akikkel kapcsolatban állunk.", "simplex-unique-overlay-card-1-p-1": "Más üzenetküldő hálózatoktól eltérően a SimpleX nem rendel azonosítókat a felhasználókhoz. Nem támaszkodik telefonszámokra, tartomány-alapú címekre (mint az e-mail, XMPP vagy a Matrix), felhasználónevekre, nyilvános kulcsokra vagy akár véletlenszerű számokra a felhasználók azonosításához — a SimpleX kiszolgálók üzemeltetői nem tudják, hogy hányan használják a kiszolgálóikat.", - "simplex-unique-overlay-card-1-p-2": "Az üzenetek kézbesítéséhez a SimpleX az egyirányú üzenetvárólistákat használ páronkénti névtelen címekkel, külön a fogadott és külön az elküldött üzenetek számára, általában különböző kiszolgálókon keresztül.", - "simplex-unique-overlay-card-1-p-3": "Ez a kialakítás védi partnerének adatait, elrejtve azt a SimpleX hálózat kiszolgálói és a külső megfigyelők elől. Az IP-címe elrejtésének érdekében aTor hálózaton keresztül is kapcsolódhat a SimpleX kiszolgálókhoz.", - "simplex-unique-overlay-card-2-p-1": "Mivel ön nem rendelkezik azonosítóval a SimpleX hálózaton, senki sem tud kapcsolatba lépni önnel, hacsak nem oszt meg egy egyszeri vagy ideiglenes felhasználói címet, például QR-kódot vagy hivatkozást.", + "simplex-unique-overlay-card-1-p-2": "Az üzenetek kézbesítéséhez a SimpleX egyirányú várólistákat használ az üzenetekhez, páronkénti, névtelen címekkel, külön a fogadott és külön az elküldött üzenetekhez, általában különböző kiszolgálókon keresztül.", + "simplex-unique-overlay-card-1-p-3": "Ez a kialakítás védi partnerei adatait, elrejtve azt a SimpleX hálózat kiszolgálói és a külső megfigyelők elől. Az IP-címe elrejtésének érdekében a Tor hálózaton keresztül is kapcsolódhat a SimpleX kiszolgálókhoz.", + "simplex-unique-overlay-card-2-p-1": "Mivel senki sem rendelkezik azonosítóval a SimpleX hálózaton, ezért senki sem tud kapcsolatba lépni Önnel, hacsak nem oszt meg egy egyszeri vagy ideiglenes felhasználói címet, például QR-kódot vagy hivatkozást.", "simplex-unique-overlay-card-2-p-2": "Még a felhasználói cím használata esetén is, aminek használata nem kötelező – ugyanakkor ez a kéretlen kapcsolatkérelmek küldésére is használható – módosíthatja vagy teljesen törölheti a címet anélkül, hogy elveszítené a meglévő kapcsolatait.", "simplex-unique-overlay-card-3-p-1": "A SimpleX Chat az összes felhasználói adatot kizárólag a klienseken tárolja egy hordozható titkosított adatbázis-formátumban, amely exportálható és átvihető bármely más támogatott eszközre.", - "simplex-unique-overlay-card-3-p-2": "A végpontok között titkosított üzenetek átmenetileg a SimpleX továbbítókiszolgálóin tartózkodnak, amíg be nem érkeznek a címzetthez, majd automatikusan véglegesen törlődnek onnan.", + "simplex-unique-overlay-card-3-p-2": "A végpontok között titkosított üzenetek átmenetileg a SimpleX továbbítókiszolgálóin tárolódnak, amíg meg nem érkeznek a címzetthez, majd automatikusan véglegesen törlődnek onnan.", "simplex-unique-overlay-card-3-p-3": "A föderált hálózatok kiszolgálóitól (e-mail, XMPP vagy Matrix) eltérően a SimpleX kiszolgálók nem tárolják a felhasználói fiókokat, csak továbbítják az üzeneteket, így védve mindkét fél magánéletét.", "simplex-unique-overlay-card-3-p-4": "A küldött és a fogadott kiszolgálóforgalom között nincsenek közös azonosítók vagy titkosított szövegek — ha bárki megfigyeli, nem tudja könnyen megállapítani, hogy ki kivel kommunikál, még akkor sem, ha a TLS-t kompromittálják.", "simplex-unique-overlay-card-4-p-1": "Használhatja a SimpleXet a saját kiszolgálóival, és továbbra is kommunikálhat azokkal, akik az előre beállított kiszolgálókat használják az alkalmazásban.", - "simplex-unique-overlay-card-4-p-2": "A SimpleX hálózat nyitott protokollt használ és SDK-t biztosít a csevegési botok létrehozásához, lehetővé téve olyan szolgáltatások megvalósítását, amelyekkel a felhasználók a SimpleX Chat alkalmazásokon keresztül léphetnek kapcsolatba — mi már nagyon várjuk, hogy milyen SimpleX szolgáltatásokat készítenek a lelkes közreműködők.", - "simplex-unique-overlay-card-4-p-3": "Ha a SimpleX hálózatra való fejlesztést fontolgatja, például a SimpleX alkalmazások felhasználóinak szánt csevegési botot, vagy a SimpleX Chat könyvtárbotjának integrálását más mobilalkalmazásba, lépjen velünk kapcsolatba, ha bármilyen tanácsot vagy támogatást szeretne kapni.", - "simplex-unique-card-1-p-1": "A SimpleX megvédi a profilhoz tartozó kapcsolatokat és metaadatokat, elrejtve azokat a SimpleX hálózat kiszolgálói és a megfigyelők elől.", + "simplex-unique-overlay-card-4-p-2": "A SimpleX hálózat nyitott protokollt használ és SDK-t biztosít a csevegési botok létrehozásához, lehetővé téve olyan szolgáltatások megvalósítását, amelyekkel a felhasználók a SimpleX Chat alkalmazásokon keresztül léphetnek kapcsolatba — mi már nagyon várjuk, hogy milyen SimpleX szolgáltatásokat készítenek a lelkes közreműködők.", + "simplex-unique-overlay-card-4-p-3": "Ha a SimpleX hálózatra való fejlesztést fontolgatja, például a SimpleX alkalmazások felhasználóinak szánt csevegési botot, vagy a SimpleX Chat könyvtárbotjának integrálását más mobilalkalmazásba, lépjen velünk kapcsolatba, ha bármilyen tanácsot vagy támogatást szeretne kapni.", + "simplex-unique-card-1-p-1": "A SimpleX megvédi a profilhoz tartozó partnereket és azok metaadatait is, elrejtve azokat a SimpleX hálózat kiszolgálói és a megfigyelők elől.", "simplex-unique-card-1-p-2": "Minden más létező üzenetküldő hálózattól eltérően a SimpleX nem rendelkezik a felhasználókhoz rendelt azonosítókkal — még véletlenszerű számokkal sem.", - "simplex-unique-card-2-p-1": "Mivel a SimpleX hálózaton nincs azonosítója vagy állandó címe, senki sem tud kapcsolatba lépni Önnel, hacsak nem oszt meg egy egyszeri vagy ideiglenes felhasználói címet, például QR-kódot vagy hivatkozást.", + "simplex-unique-card-2-p-1": "Mivel a SimpleX hálózaton senkinek sincs azonosítója vagy állandó címe, ezért senki sem tud kapcsolatba lépni Önnel, hacsak nem oszt meg egy egyszeri vagy ideiglenes felhasználói címet, például QR-kódot vagy hivatkozást.", "simplex-unique-card-3-p-1": "A SimpleX Chat az összes felhasználói adatot kizárólag a klienseken tárolja egy hordozható titkosított adatbázis-formátumban —, amely exportálható és átvihető bármely más támogatott eszközre.", "simplex-unique-card-3-p-2": "A végpontok között titkosított üzenetek átmenetileg a SimpleX továbbítókiszolgálóin tartózkodnak, amíg be nem érkeznek a címzetthez, majd automatikusan véglegesen törlődnek onnan.", "simplex-unique-card-4-p-1": "A SimpleX hálózat teljesen decentralizált és független bármely kriptopénztől vagy bármely más hálózattól, kivéve az internetet.", @@ -137,23 +137,23 @@ "join-us-on-GitHub": "Csatlakozzon hozzánk a GitHubon", "donate-here-to-help-us": "Adományozzon és segítsen nekünk", "sign-up-to-receive-our-updates": "Regisztráljon a hírleveleinkre, hogy ne maradjon le semmiről", - "enter-your-email-address": "Adja meg az e-mail-címét", + "enter-your-email-address": "Adjon meg egy e-mail-címet", "get-simplex": "A SimpleX számítógépes alkalmazásának letöltése", - "why-simplex-is-unique": "Miért egyedülálló a SimpleX", + "why-simplex-is-unique": "Miért egyedülálló a SimpleX", "learn-more": "Tudjon meg többet", "more-info": "További információ", "hide-info": "Információ elrejtése", "contact-hero-subheader": "Olvassa be a QR-kódot a SimpleX Chat alkalmazással a telefonjával vagy táblagépével.", - "contact-hero-p-1": "A hivatkozásban szereplő nyilvános kulcsokat és az üzenetek várólistájának címét NEM küldi el a hálózaton keresztül, amikor megtekinti ezt az oldalt — azokat, a hivatkozás webcímének kivonattöredéke tartalmazza.", + "contact-hero-p-1": "A hivatkozásban szereplő nyilvános kulcsokat és az üzenetek várólistájának címe NEM kerül elküldésre a hálózaton keresztül, amikor megtekinti ezt az oldalt — azokat, a hivatkozás webcímének kivonattöredéke tartalmazza.", "contact-hero-p-2": "Még nem töltötte le a SimpleX Chatet?", "contact-hero-p-3": "Az alkalmazás letöltéséhez használja az alábbi hivatkozásokat.", "scan-qr-code-from-mobile-app": "QR-kód beolvasása mobilalkalmazásból", "to-make-a-connection": "A kapcsolat létrehozásához:", "install-simplex-app": "Telepítse a SimpleX alkalmazást", "open-simplex-app": "Simplex alkalmazás megnyitása", - "tap-the-connect-button-in-the-app": "Koppintson a „kapcsolódás” gombra az alkalmazásban", + "tap-the-connect-button-in-the-app": "Koppintson a „kapcsolódás” gombra az alkalmazásban", "scan-the-qr-code-with-the-simplex-chat-app": "Olvassa be a QR-kódot a SimpleX Chat alkalmazással", - "scan-the-qr-code-with-the-simplex-chat-app-description": "A hivatkozásban szereplő nyilvános kulcsokat és az üzenetek várólistájának címét NEM küldjük el a hálózaton keresztül, amikor ezt az oldalt megtekinti —
ezek a hivatkozás webcímének kivonattöredékében szerepelnek.", + "scan-the-qr-code-with-the-simplex-chat-app-description": "A hivatkozásban szereplő nyilvános kulcsokat és az üzenetek várólistájának címe NEM kerül elküldésre a hálózaton keresztül, amikor ezt az oldalt megtekinti —
ezek a hivatkozás webcímének kivonattöredékében szerepelnek.", "installing-simplex-chat-to-terminal": "A SimpleX chat telepítése a terminálhoz", "use-this-command": "Használja ezt a parancsot:", "see-simplex-chat": "Az utasításokat megtekintheti a SimpleX Chat", @@ -163,23 +163,23 @@ "if-you-already-installed": "Ha már telepítette a", "simplex-chat-for-the-terminal": "SimpleX Chat a terminálhoz", "copy-the-command-below-text": "másolja be az alábbi parancsot, és használja a csevegésben:", - "privacy-matters-section-header": "Miért számít az adatvédelem", - "privacy-matters-section-subheader": "A metaadatok — pédául, hogy kivel beszélget — védelmének megőrzése biztonságot nyújt a következők ellen:", + "privacy-matters-section-header": "Miért számít az adatvédelem", + "privacy-matters-section-subheader": "A metaadatok — például, hogy kivel beszélget — védelmének megőrzése biztonságot nyújt a következők ellen:", "privacy-matters-section-label": "Győződjön meg arról, hogy az üzenetváltó-alkalmazás amit használ nem fér hozzá az adataihoz!", - "simplex-private-section-header": "Mitől lesz a SimpleX privát", - "simplex-network-section-header": "SimpleX hálózat", + "simplex-private-section-header": "Mitől lesz a SimpleX privát", + "simplex-network-section-header": "SimpleX hálózat", "simplex-network-section-desc": "A Simplex Chat a P2P- és a föderált hálózatok előnyeinek kombinálásával biztosítja a legjobb adatvédelmet.", "simplex-network-1-desc": "Minden üzenet a kiszolgálókon keresztül kerül elküldésre, ami jobb metaadat-védelmet és megbízható aszinkron üzenetkézbesítést biztosít, miközben elkerülhető a sok", "simplex-network-2-header": "A föderált hálózatokkal ellentétben", - "simplex-network-2-desc": "A SimpleX továbbítókiszolgálói NEM tárolnak felhasználói profilokat, kapcsolatokat és kézbesített üzeneteket, NEM kapcsolódnak egymáshoz, és NINCS kiszolgálókönyvtár.", + "simplex-network-2-desc": "A SimpleX továbbítókiszolgálói NEM tárolnak felhasználói profilokat, kapcsolatokat és kézbesített üzeneteket, NEM kapcsolódnak egymáshoz, és NINCS kiszolgálójegyzék.", "simplex-network-3-header": "SimpleX hálózat", - "simplex-network-3-desc": "A kiszolgálók egyirányú üzenetvárólistákat biztosítanak a felhasználók összekapcsolásához, de nem látják a hálózati kapcsolati gráfot; azt csak a felhasználók látják.", + "simplex-network-3-desc": "a kiszolgálók egyirányú várólistákat biztosítanak a felhasználók összekapcsolásához, de nem látják a hálózati kapcsolati gráfot; azt csak a felhasználók látják.", "comparison-section-header": "Összehasonlítás más protokollokkal", - "protocol-1-text": "Signal, nagy platformok", + "protocol-1-text": "Signal, és a nagy platformok", "protocol-2-text": "XMPP, Matrix", "protocol-3-text": "P2P-protokollok", "comparison-point-1-text": "Globális személyazonosságot igényel", - "comparison-point-2-text": "MITM lehetősége", + "comparison-point-2-text": "A MITM lehetősége", "comparison-point-4-text": "Egyetlen vagy központosított hálózat", "comparison-point-5-text": "Központi komponens vagy más hálózati szintű támadás", "no": "Nem", @@ -191,7 +191,7 @@ "comparison-section-list-point-1": "Általában telefonszám alapján, néhány esetben felhasználónév alapján", "comparison-section-list-point-2": "DNS-alapú címek", "comparison-section-list-point-3": "Nyilvános kulcs vagy más globális egyedi azonosító", - "comparison-section-list-point-4a": "A SimpleX továbbítókiszolgálói nem veszélyeztethetik az e2e titkosítást. Hitelesítse a biztonsági kódot a sávon kívüli csatorna elleni támadások veszélyeinek csökkentésére", + "comparison-section-list-point-4a": "A SimpleX továbbítókiszolgálói nem veszélyeztethetik a végpontok közötti titkosítást. Hitelesítse a biztonsági kódot a sávon kívüli csatorna elleni támadások veszélyeinek csökkentésére", "comparison-section-list-point-4": "Ha az üzemeltetett kiszolgálók veszélybe kerülnek. Hitelesítse a biztonsági kódot a Signal vagy más biztonságos üzenetküldő alkalmazás segítségével a támadások veszélyeinek csökkentésére", "comparison-section-list-point-5": "Nem védi a felhasználók metaadatait", "comparison-section-list-point-6": "Bár a P2P elosztott, de nem föderált — egyetlen hálózatként működnek", @@ -211,26 +211,26 @@ "docs-dropdown-3": "Hozzáférés a csevegési adatbázishoz", "docs-dropdown-8": "SimpleX csoportjegyzék", "docs-dropdown-9": "Letöltések", - "f-droid-page-simplex-chat-repo-section-text": "Ha hozzá szeretné adni az F-Droid klienséhez, olvassa be a QR-kódot, vagy használja ezt a webcímet:", + "f-droid-page-simplex-chat-repo-section-text": "Ha hozzá szeretné adni az F-Droid klienséhez, olvassa be a QR-kódot, vagy használja ezt a webcímet:", "signing-key-fingerprint": "Az aláírókulcs ujjlenyomata (SHA-256)", "f-droid-org-repo": "F-Droid-tároló", "stable-versions-built-by-f-droid-org": "F-Droid.org által készített stabil verziók", "releases-to-this-repo-are-done-1-2-days-later": "A kiadások ebben a tárolóban néhány napot késnek", - "f-droid-page-f-droid-org-repo-section-text": "A SimpleX Chat és az F-Droid-tároló különböző kulcsokkal írják alá az összeállításokat. A váltáshoz exportálja a csevegési adatbázist és telepítse újra az alkalmazást.", + "f-droid-page-f-droid-org-repo-section-text": "A SimpleX Chat és az F-Droid-tároló különböző kulcsokkal írják alá az összeállításokat. A váltáshoz exportálja a csevegési adatbázist és telepítse újra az alkalmazást.", "jobs": "Csatlakozzon a csapathoz", "please-enable-javascript": "Engedélyezze a JavaScriptet a QR-kód megjelenítéséhez.", "please-use-link-in-mobile-app": "Használja a mobilalkalmazásban található hivatkozást", "contact-hero-header": "Kapott egy meghívót a SimpleX Chaten való beszélgetéshez", "invitation-hero-header": "Kapott egy egyszer használható meghívót a SimpleX Chaten való beszélgetéshez", - "simplex-network-overlay-card-1-li-4": "A P2P-megvalósításokat egyes internetszolgáltatók blokkolhatják (mint például a BitTorrent). A SimpleX átvitel-független — a szabványos webes protokollokon, például WebSocketsen keresztül is működik.", + "simplex-network-overlay-card-1-li-4": "A P2P-megvalósításokat egyes internetszolgáltatók blokkolhatják (mint például a BitTorrent). A SimpleX átvitel-független — a szabványos webes protokollokon, például WebSocketsen keresztül is működik.", "simplex-private-card-4-point-2": "A SimpleX, Tor hálózaton keresztüli használatához telepítse az Orbot alkalmazást és engedélyezze a SOCKS5 proxyt (vagy a VPN-t az iOS-ban).", "simplex-private-card-5-point-1": "A SimpleX minden titkosítási réteghez tartalomkitöltést használ az üzenetméretre irányuló támadások meghiúsítása érdekében.", "simplex-private-card-5-point-2": "A kiszolgálók és a hálózatot megfigyelők számára a különböző méretű üzenetek egyformának tűnnek.", "privacy-matters-1-title": "Hirdetés és árdiszkrimináció", - "hero-overlay-card-1-p-3": "Ön határozza meg, hogy melyik kiszolgáló(ka)t használja az üzenetek fogadására, a kapcsolatokhoz — azokat a kiszolgálókat, amelyeket az üzenetek küldésére használ. Minden beszélgetés két különböző kiszolgálót használ.", - "simplex-network-overlay-card-1-p-1": "A P2P üzenetküldő protokollok és alkalmazások számos problémával küzdenek, amelyek miatt kevésbé megbízhatóak, mint a SimpleX, bonyolultabb az elemzésük és többféle támadással szemben sebezhetőek.", - "chat-bot-example": "Példa csevegési botra", - "simplex-private-3-title": "Biztonságos, hitelesített
TLS adatátvitel", + "hero-overlay-card-1-p-3": "Ön határozza meg, hogy melyik kiszolgáló(ka)t használja az üzenetek fogadására, a partnereihez — azokat a kiszolgálókat, amelyeket az üzenetek küldésére használ. Minden beszélgetés két különböző kiszolgálót használ.", + "simplex-network-overlay-card-1-p-1": "A P2P üzenetküldő protokollok és alkalmazások számos problémával küzdenek, amelyek miatt kevésbé megbízhatóak, mint a SimpleX, bonyolultabb az elemzésük és többféle támadással szemben sebezhetőek.", + "chat-bot-example": "Példa egy csevegési botra", + "simplex-private-3-title": "Biztonságos, hitelesített
TLS-adatátvitel", "github-repository": "GitHub-tárolójában", "tap-to-close": "Bezárás", "simplex-network-1-header": "A P2P-hálózatokkal ellentétben", @@ -243,7 +243,7 @@ "docs-dropdown-6": "WebRTC-kiszolgálók", "docs-dropdown-7": "SimpleX Chat honosítása", "docs-dropdown-10": "Átláthatóság", - "docs-dropdown-11": "GY.I.K.", + "docs-dropdown-11": "GYIK", "docs-dropdown-12": "Biztonság", "newer-version-of-eng-msg": "Ennek az oldalnak van egy újabb angol nyelvű változata.", "click-to-see": "Kattintson a megtekintéséhez", @@ -258,32 +258,32 @@ "docs-dropdown-14": "SimpleX üzleti célra", "directory": "Csoportjegyzék", "about-and-contact-us": "Névjegy és kapcsolat", - "index-hero-h1": "
Legyen
szabad
", + "index-hero-h1": "Legyen
szabad", "index-hero-p1": "Az első olyan hálózat, ahol Ön a tulajdonosa saját identitásának, partnereinek és csoportjainak.", "index-hero-download-desktop-btn-title": "SimpleX számítógépes alkalmazásának letöltése", - "index-security-assessment-title": "biztonsági felmérés", + "index-security-assessment-title": "Biztonsági auditok", "index-security-review-2022-title": "Biztonsági audit 2022", "index-security-review-2024-title": "Biztonsági audit 2024", "index-security-audits-label": "Biztonsági
auditok", - "index-publications-heise-title": "kiadvány", + "index-publications-heise-title": "A Heise Online kiadványai", "index-hero-h2": "Az Ön kommunikációjának
szabadsága és biztonsága", "index-testflight-title": "Nyilvános betekintés az iOS alkalmazás fejlesztésébe a TestFlighton", - "index-f-droid-title": "SimpleX F-Droid-tároló", - "index-publications-privacy-guides-title": "ajánlások", + "index-f-droid-title": "SimpleX alkalmazás az F-Droidon keresztül", + "index-publications-privacy-guides-title": "A Privacy Guides üzenetváltó ajánlásai", "index-publications-whonix-title": "Whonix ajánlás", - "index-publications-kuketz-title": "áttekintés", - "index-publications-optout-title": "podcast interjú", + "index-publications-kuketz-title": "Áttekintette: Mike Kuketz", + "index-publications-optout-title": "OptOut podcast interjú", "worlds-most-secure-messaging": "A világ legbiztonságosabb üzenetváltó alkalmazása", "index-messaging-p1": "Az üzenetváltás a SimpleXben élvonalbeli végpontok közötti titkosítással rendelkezik.", - "index-messaging-p2": "Az Ön biztonsága és magánszférájának védelme érdekében a kiszolgálók nem látják az üzeneteit, sem azt, hogy kivel beszélget.", + "index-messaging-p2": "Az Ön biztonsága és magánszférájának védelme érdekében a kiszolgálók nem látják az üzeneteit, és azt sem, hogy kivel beszélget.", "index-messaging-cta": "Tudjon meg többet a SimpleX üzenetváltó alkalmazásról", - "index-nextweb-h2": "A következő háló
Az Öné", - "index-nextweb-p1": "A SimpleX arra a meggyőződésre épül, hogy Önnek kell birtokolnia a saját identitását, a kapcsolatait és a közösségeit.", + "index-nextweb-h2": "Legyen az Öné
A jövő hálózata", + "index-nextweb-p1": "A SimpleX arra a meggyőződésre épül, hogy Önnek kell birtokolnia a saját identitását és a kapcsolatot partnereivel és a közösségeivel.", "index-nextweb-p2": "A nyílt és decentralizált hálózat lehetővé teszi, hogy kapcsolatba lépjen másokkal és ötleteket osszon meg: legyen szabad biztonságban.", "index-token-h2": "Időtálló közösségek", "index-token-p1": "A jövőben közösségi utalványokkal támogathatja a kedvenc csoportjait.", "index-token-p2": "Az utalványokkal fizetni tudja a kiszolgálókat, hogy a közösségek szabadok és függetlenek maradhassanak.", - "index-token-cta": "Tudjon meg többet, és szerezzen ingyenes NFT-t
20% kedvezménnyel a SimpleX Tokenre!", + "index-token-cta": "Tudjon meg többet, és szerezzen ingyenes NFT-t
az előzetes tesztelésért.", "index-roadmap-h2": "A SimpleX ütemterve a szabad internethez", "index-roadmap-2025": "2025", "index-roadmap-2025-title": "Skálázódás nagy közösségekre", @@ -296,20 +296,20 @@ "index-roadmap-2027-desc": "Eszközök a közösségei népszerűsítéséhez", "index-directory-h2": "Csatlakozzon a SimpleX közösségekhez", "index-directory-p1": "Már emberek százezrei bíznak a SimpleXen való üzenetváltásban.", - "index-directory-p2": "Találja meg kedvenc közösségeit a SimpleX csoportjegyzékében, vagy hozza létre a sajátját!", + "index-directory-p2": "Találja meg a kedvenc közösségeit a SimpleX csoportjegyzékében, vagy hozza létre a saját csoportját!", "index-directory-cta": "SimpleX csoportjegyzék megtekintése", "index-directory-users-group-title": "SimpleX felhasználók csoportja", - "how-secure-comparison-title": "Mennyire biztonságos a végpontok közötti titkosítás a különböző üzenetváltó alkalmazásokban?", - "how-secure-message-padding": "Üzenetkitöltés", + "how-secure-comparison-title": "A végpontok közötti titkosítás összehasonlítása más üzenetváltó alkalmazásokkal", + "how-secure-message-padding": "Tartalomkitöltés", "how-secure-repudiation-deniability": "Letagadhatóság", "how-secure-forward-secrecy": "Kompromittálás előtti titkosságvédelem", - "how-secure-break-in-recovery": "Kompromittálás utáni biztonság", + "how-secure-break-in-recovery": "Kompromittálás utáni titkosságvédelem", "how-secure-two-factor-key-exchange": "Kétlépcsős kulcscsere", - "how-secure-post-quantum-hybrid-crypto": "Hibrid kriptográfia a kvantum számítógépek megjelenése utánra", - "messengers-comparison-section-list-point-1": "A Briar 1024 bájtra, a Signal pedig 160 bájtra kerekítve tölti fel az üzeneteket", + "how-secure-post-quantum-hybrid-crypto": "Kvantumbiztos, hibrid kriptográfia", + "messengers-comparison-section-list-point-1": "A Briar 1024 bájtra, a Signal pedig 160 bájtra kerekítve tölti ki az üzenetek tartalmát", "messengers-comparison-section-list-point-2": "A letagadhatóság nem foglalja magába a kliens és a kiszolgáló közötti kapcsolatot.", "messengers-comparison-section-list-point-3": "Úgy tűnik, hogy a kriptográfiai aláírások használata rontja a letagadhatóságot, de ez tisztázásra szorul.", "messengers-comparison-section-list-point-4": "A többeszközös megvalósítás rontja a dupla racsni kompromittálás utáni biztonságát", "messengers-comparison-section-list-point-5": "A kétlépcsős kulcscsere nem követelmény a biztonsági kód ellenőrzéséhez.", - "messengers-comparison-section-list-point-6": "A posztkvantum kulcscsere „ritka” — csak a racsnis lépések egy részét védi." + "messengers-comparison-section-list-point-6": "A kvantumbiztos kulcscsere „ritka” — csak a racsnis lépések egy részét védi." } diff --git a/website/langs/id.json b/website/langs/id.json index 51bd14f976..4c3320c204 100644 --- a/website/langs/id.json +++ b/website/langs/id.json @@ -30,7 +30,7 @@ "simplex-explained-tab-2-text": "2. Bagaimana cara kerjanya", "simplex-chat-protocol": "Protokol SimpleX Chat", "hero-overlay-2-title": "Mengapa ID pengguna buruk untuk privasi?", - "copyright-label": "© 2020-2025 SimpleX | Open-Source Project", + "copyright-label": "© 2020-2025 SimpleX Chat | Open-Source Project", "simplex-explained-tab-3-text": "3. Apa yang dilihat server", "smp-protocol": "Protokol SMP", "please-use-link-in-mobile-app": "Mohon gunakan tautan di aplikasi seluler", diff --git a/website/langs/it.json b/website/langs/it.json index b7b11ccd72..b4db5e2399 100644 --- a/website/langs/it.json +++ b/website/langs/it.json @@ -10,7 +10,7 @@ "simplex-explained-tab-3-p-1": "I server hanno credenziali anonime separate per ogni coda e non sanno a quali utenti appartengano.", "chat-protocol": "Protocollo di chat", "donate": "Dona", - "copyright-label": "© 2020-2025 SimpleX | Progetto Open-Source", + "copyright-label": "© 2020-2025 SimpleX Chat | Progetto Open-Source", "simplex-chat-protocol": "Protocollo di SimpleX Chat", "terminal-cli": "Terminale CLI", "terms-and-privacy-policy": "Informativa sulla privacy", @@ -56,24 +56,24 @@ "hero-overlay-card-1-p-1": "Molti utenti hanno chiesto: se SimpleX non ha identificatori utente, come può sapere dove recapitare i messaggi? ", "hero-overlay-card-1-p-3": "Sei tu a definire quale/i server usare per ricevere i messaggi, i tuoi contatti i server che usi per inviare loro i messaggi. È probabile che ogni conversazione utilizzi due server diversi.", "hero-overlay-card-1-p-5": "Solo i dispositivi client conservano profili utente, contatti e gruppi; i messaggi vengono inviati con crittografia end-to-end a 2 livelli.", - "hero-overlay-card-1-p-6": "Maggiori informazioni nel libro bianco di SimpleX.", + "hero-overlay-card-1-p-6": "Maggiori informazioni nel libro bianco di SimpleX.", "hero-overlay-card-2-p-2": "Potrebbero quindi correlare queste informazioni con i social network pubblici esistenti e determinare alcune identità reali.", "hero-overlay-card-2-p-4": "SimpleX protegge da questi attacchi non avendo alcun ID utente per design. Inoltre, se usi la modalità di modalità in incognito, il tuo nome mostrato sarà diverso per ogni contatto, evitando dati condivisi tra di loro.", "simplex-network-overlay-card-1-li-2": "Il design di SimpleX, a differenza della maggior parte delle reti P2P, non ha identificatori utente globali di alcun tipo, nemmeno temporanei, e usa solo identificatori temporanei a coppie, garantendo una maggiore protezione dell'anonimato e dei metadati.", - "simplex-network-overlay-card-1-li-4": "Le implementazioni P2P possono essere bloccate da alcuni fornitori di internet (come BitTorrent). SimpleX è indipendente dal trasporto — può funzionare su protocolli web standard, es. WebSocket.", + "simplex-network-overlay-card-1-li-4": "Le implementazioni P2P possono essere bloccate da alcuni fornitori di internet (come BitTorrent). SimpleX è indipendente dal trasporto — può funzionare su protocolli web standard, es. WebSocket.", "hero-overlay-card-2-p-1": "Quando gli utenti hanno identità permanenti, anche se si tratta solo di un numero casuale, come un Session ID, c'è il rischio che il fornitore o un malintenzionato possano osservare come gli utenti sono connessi e quanti messaggi inviano.", - "simplex-network-overlay-card-1-li-6": "Le reti P2P possono essere vulnerabili all'attacco DRDoS, quando i client possono ritrasmettere e amplificare il traffico, con conseguente \"denial of service\" a livello di rete. I client SimpleX si limitano a inoltrare il traffico da una connessione nota e non possono essere usati da un aggressore per amplificare il traffico nell'intera rete.", + "simplex-network-overlay-card-1-li-6": "Le reti P2P possono essere vulnerabili all'attacco DRDoS, quando i client possono ritrasmettere e amplificare il traffico, con conseguente \"denial of service\" a livello di rete. I client SimpleX si limitano a inoltrare il traffico da una connessione nota e non possono essere usati da un aggressore per amplificare il traffico nell'intera rete.", "privacy-matters-overlay-card-1-p-4": "La rete di SimpleX protegge la privacy delle tue connessioni meglio di qualsiasi alternativa, impedendo completamente che il tuo grafico sociale sia disponibile a qualsiasi azienda o organizzazione. Anche quando le persone usano i server preconfigurati in SimpleX Chat, gli operatori dei server non conoscono il numero di utenti o le loro connessioni.", "privacy-matters-overlay-card-2-p-3": "SimpleX è la prima rete che non ha alcun identificatore utente per design, proteggendo così il tuo grafico delle connessioni meglio di qualsiasi alternativa conosciuta.", "privacy-matters-overlay-card-3-p-1": "Tutti dovrebbero preoccuparsi della privacy e della sicurezza delle proprie comunicazioni — conversazioni innocue possono metterti in pericolo, anche se non hai nulla da nascondere.", - "privacy-matters-overlay-card-3-p-3": "Le persone comuni vengono arrestate per ciò che condividono online, anche tramite i loro account \"anonimi\", anche nei Paesi democratici.", + "privacy-matters-overlay-card-3-p-3": "Le persone comuni vengono arrestate per ciò che condividono online, anche tramite i loro account \"anonimi\", anche nei Paesi democratici.", "simplex-unique-overlay-card-1-p-3": "Questo design protegge la privacy di chi stai comunicando, nascondendola ai server della rete SimpleX e a qualsiasi osservatore. Per nascondere il tuo indirizzo IP ai server, puoi connetterti ai server SimpleX tramite Tor.", "simplex-unique-overlay-card-2-p-1": "Poiché non hai alcun identificatore sulla rete SimpleX, nessuno può contattarti a meno che tu non condivida un indirizzo utente una tantum o temporaneo, come un codice QR o un link.", "simplex-unique-overlay-card-2-p-2": "Anche l'indirizzo utente opzionale, che può essere usato per inviare richieste di contatto spam, è possibile modificarlo o eliminarlo completamente senza perdere alcuna connessione.", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat conserva tutti i dati utente solo sui dispositivi client usando un formato trasferibile di database crittografato che può essere esportato e trasferito su qualsiasi dispositivo supportato.", "simplex-unique-overlay-card-3-p-2": "I messaggi crittografati end-to-end vengono conservati temporaneamente sui server di inoltro SimpleX fino alla ricezione, quindi vengono eliminati definitivamente.", "simplex-unique-overlay-card-4-p-1": "Puoi usare SimpleX con i tuoi server personali e continuare a comunicare con le persone che usano i server preconfigurati nelle app.", - "simplex-unique-overlay-card-4-p-3": "Se stai pensando di sviluppare per la rete SimpleX, ad esempio, il chat bot per gli utenti dell'app SimpleX o l'integrazione della libreria SimpleX Chat nelle tue app mobili, contattaci per qualsiasi consiglio e supporto.", + "simplex-unique-overlay-card-4-p-3": "Se stai pensando di sviluppare per la rete SimpleX, ad esempio, il chat bot per gli utenti dell'app SimpleX o l'integrazione della libreria SimpleX Chat nelle tue app mobili, contattaci per qualsiasi consiglio e supporto.", "simplex-unique-card-1-p-1": "SimpleX protegge la privacy del tuo profilo, contatti e metadati, nascondendoli ai server della rete SimpleX e ad eventuali osservatori.", "simplex-unique-card-1-p-2": "A differenza di qualsiasi altra rete di messaggistica esistente, SimpleX non ha identificatori assegnati agli utenti — nemmeno numeri casuali.", "simplex-unique-card-2-p-1": "Poiché non hai un identificatore o un indirizzo fisso sulla rete SimpleX, nessuno può contattarti a meno che tu non condivida un indirizzo utente una tantum o temporaneo, come codice un QR o un link.", @@ -82,7 +82,7 @@ "we-invite-you-to-join-the-conversation": "Ti invitiamo ad unirti alla conversazione", "enter-your-email-address": "Inserisci il tuo indirizzo email", "get-simplex": "Ottieni l'app desktop di SimpleX", - "why-simplex-is-unique": "Perché SimpleX è unico", + "why-simplex-is-unique": "Perché SimpleX è unico", "learn-more": "Maggiori informazioni", "more-info": "Mostra info", "hide-info": "Nascondi info", @@ -96,7 +96,7 @@ "install-simplex-app": "Installa l'app SimpleX", "connect-in-app": "Connettiti nell'app", "open-simplex-app": "Apri l'app SimpleX", - "tap-the-connect-button-in-the-app": "Tocca il pulsante 'connetti' nell'app", + "tap-the-connect-button-in-the-app": "Tocca il pulsante \"connetti\" nell'app", "scan-the-qr-code-with-the-simplex-chat-app": "Scansiona il codice QR con l'app SimpleX Chat", "installing-simplex-chat-to-terminal": "Installazione di SimpleX Chat nel terminale", "use-this-command": "Usa questo comando:", @@ -105,9 +105,9 @@ "the-instructions--source-code": "per le istruzioni su come scaricarlo o compilarlo dal codice sorgente.", "if-you-already-installed-simplex-chat-for-the-terminal": "Se hai già installato SimpleX Chat per il terminale", "if-you-already-installed": "Se hai già installato", - "privacy-matters-section-subheader": "Preservare la privacy dei tuoi metadati — con chi parli — ti protegge da:", + "privacy-matters-section-subheader": "Preservare la privacy dei tuoi metadati — con chi parli — ti protegge da:", "privacy-matters-section-label": "Assicurati che il tuo messenger non possa accedere ai tuoi dati!", - "simplex-network-section-header": "Rete di SimpleX", + "simplex-network-section-header": "Rete di SimpleX", "simplex-network-section-desc": "Simplex Chat offre la migliore privacy combinando i vantaggi del P2P e delle reti federate.", "simplex-network-1-header": "A differenza delle reti P2P", "simplex-network-1-overlay-linktext": "problemi delle reti P2P", @@ -173,20 +173,20 @@ "simplex-private-card-3-point-3": "Ripresa della connessione disattivata per evitare attacchi alla sessione.", "hero-overlay-card-1-p-4": "Questo design impedisce la fuoriuscita di metadati degli utenti a livello di applicazione. Per aumentare ulteriormente la privacy e proteggere il tuo indirizzo IP puoi connetterti ai server di messaggistica tramite Tor.", "hero-overlay-card-2-p-3": "Anche con le app più private che usano i servizi Tor v3, se parli con due contatti diversi tramite lo stesso profilo, questi possono dimostrare di essere connessi alla stessa persona.", - "simplex-network-overlay-card-1-p-1": "I protocolli e le app di messaggistica P2P hanno diversi problemi che li rendono meno affidabili di SimpleX, più complessi da analizzare e vulnerabili a diversi tipi di attacco.", - "simplex-network-overlay-card-1-li-1": "Le reti P2P si basano su alcune varianti di DHT per instradare i messaggi. I progetti DHT devono bilanciare la garanzia di consegna e la latenza. SimpleX ha sia una migliore garanzia di consegna che una latenza minore rispetto al P2P, perché il messaggio può essere passato in modo ridondante attraverso diversi server in parallelo, usando i server scelti dal destinatario. Nelle reti P2P il messaggio viene passato attraverso i nodi O(log N) in sequenza, usando nodi scelti dall'algoritmo.", - "simplex-network-overlay-card-1-li-3": "Il P2P non risolve il problema dell'attacco MITM e la maggior parte delle implementazioni esistenti non usa messaggi fuori banda per lo scambio iniziale di chiavi. SimpleX usa messaggi fuori banda o, in alcuni casi, connessioni sicure e attendibili preesistenti per lo scambio iniziale di chiavi.", + "simplex-network-overlay-card-1-p-1": "I protocolli e le app di messaggistica P2P hanno diversi problemi che li rendono meno affidabili di SimpleX, più complessi da analizzare e vulnerabili a diversi tipi di attacco.", + "simplex-network-overlay-card-1-li-1": "Le reti P2P si basano su alcune varianti di DHT per instradare i messaggi. I progetti DHT devono bilanciare la garanzia di consegna e la latenza. SimpleX ha sia una migliore garanzia di consegna che una latenza minore rispetto al P2P, perché il messaggio può essere passato in modo ridondante attraverso diversi server in parallelo, usando i server scelti dal destinatario. Nelle reti P2P il messaggio viene passato attraverso i nodi O(log N) in sequenza, usando nodi scelti dall'algoritmo.", + "simplex-network-overlay-card-1-li-3": "Il P2P non risolve il problema dell'attacco MITM e la maggior parte delle implementazioni esistenti non usa messaggi fuori banda per lo scambio iniziale di chiavi. SimpleX usa messaggi fuori banda o, in alcuni casi, connessioni sicure e attendibili preesistenti per lo scambio iniziale di chiavi.", "privacy-matters-overlay-card-1-p-1": "Molte grandi aziende usano le informazioni sulle persone con cui sei connesso per stimare il tuo reddito, venderti i prodotti di cui non hai realmente bisogno e determinare i prezzi.", - "simplex-network-overlay-card-1-li-5": "Tutte le reti P2P conosciute possono essere vulnerabili all'attacco di Sybil, perché ogni nodo è rilevabile e la rete opera come un tutt'uno. Le misure note per mitigarlo richiedono un componente centralizzato o proof-of-work costosi. La rete di SimpleX non ha la possibilità di scoprire i server, è frammentata e opera come più sottoreti isolate, rendendo impossibili gli attacchi a livello di rete.", + "simplex-network-overlay-card-1-li-5": "Tutte le reti P2P conosciute possono essere vulnerabili all'attacco di Sybil, perché ogni nodo è rilevabile e la rete opera come un tutt'uno. Le misure note per mitigarlo richiedono un componente centralizzato o proof-of-work costosi. La rete di SimpleX non ha la possibilità di scoprire i server, è frammentata e opera come più sottoreti isolate, rendendo impossibili gli attacchi a livello di rete.", "privacy-matters-overlay-card-1-p-2": "I rivenditori online sanno che le persone con redditi più bassi sono più propense a fare acquisti urgenti, quindi possono applicare prezzi più alti o rimuovere sconti.", - "privacy-matters-overlay-card-1-p-3": "Alcune società finanziarie e assicurative usano grafici sociali per determinare i tassi di interesse e i premi. Spesso ciò fa pagare di più le persone con redditi più bassi — è noto come \"premio di povertà\" .", - "privacy-matters-overlay-card-2-p-1": "Non molto tempo fa abbiamo assistito alla manipolazione delle principali elezioni da una rispettabile società di consulenza che ha usato i nostri grafici sociali per distorcere la nostra visione del mondo reale e manipolare i nostri voti.", + "privacy-matters-overlay-card-1-p-3": "Alcune società finanziarie e assicurative usano grafici sociali per determinare i tassi di interesse e i premi. Spesso ciò fa pagare di più le persone con redditi più bassi — è noto come \"premio di povertà\".", + "privacy-matters-overlay-card-2-p-1": "Non molto tempo fa abbiamo assistito alla manipolazione delle principali elezioni da una rispettabile società di consulenza che ha usato i nostri grafici sociali per distorcere la nostra visione del mondo reale e manipolare i nostri voti.", "privacy-matters-overlay-card-2-p-2": "Per essere obiettivi e prendere decisioni indipendenti devi avere il controllo del tuo spazio informativo. È possibile solo se utilizzi una rete di comunicazione privata che non ha accesso al tuo grafico sociale.", - "privacy-matters-overlay-card-3-p-2": "Una delle storie più scioccanti è l'esperienza di Mohamedou Ould Salahi descritta nel suo libro di memorie e mostrata nel film The Mauritanian. È stato rinchiuso nel campo di Guantánamo, senza processo, e lì è stato torturato per 15 anni dopo una telefonata a un suo parente in Afghanistan, sospettato di essere coinvolto negli attacchi dell'11/9, nonostante avesse vissuto in Germania per i precedenti 10 anni.", + "privacy-matters-overlay-card-3-p-2": "Una delle storie più scioccanti è l'esperienza di Mohamedou Ould Salahi descritta nel suo libro di memorie e mostrata nel film The Mauritanian. È stato rinchiuso nel campo di Guantánamo, senza processo, e lì è stato torturato per 15 anni dopo una telefonata a un suo parente in Afghanistan, sospettato di essere coinvolto negli attacchi dell'11/9, nonostante avesse vissuto in Germania per i precedenti 10 anni.", "join-us-on-GitHub": "Unisciti a noi su GitHub", "simplex-chat-for-the-terminal": "SimpleX Chat per il terminale", "privacy-matters-overlay-card-3-p-4": "Non è sufficiente usare un messenger crittografato end-to-end, tutti dovremmo usare i messenger che proteggono la privacy delle nostre reti personali — con chi siamo connessi.", - "simplex-unique-overlay-card-1-p-2": "Per recapitare i messaggi, SimpleX usa indirizzi anonimi a coppie di code di messaggi unidirezionali, separate per i messaggi ricevuti e inviati, di solito tramite server diversi.", + "simplex-unique-overlay-card-1-p-2": "Per recapitare i messaggi, SimpleX usa indirizzi anonimi a coppie di code di messaggi unidirezionali, separate per i messaggi ricevuti e inviati, di solito tramite server diversi.", "simplex-unique-overlay-card-1-p-1": "A differenza di altre reti di messaggistica, SimpleX non ha alcun identificatore assegnato agli utenti. Non si basa su numeri di telefono, indirizzi basati su domini (come email o XMPP), nomi utente, chiavi pubbliche o persino numeri casuali per identificare i suoi utenti — gli operatori dei server non sanno quante persone usano i loro server.", "simplex-unique-overlay-card-3-p-3": "A differenza dei server di reti federate (email, XMPP o Matrix), i server SimpleX non conservano gli account utente, ma trasmettono solo i messaggi, proteggendo la privacy di entrambe le parti.", "simplex-unique-card-3-p-2": "I messaggi crittografati end-to-end vengono conservati temporaneamente sui server di inoltro SimpleX fino alla ricezione, quindi vengono eliminati definitivamente.", @@ -194,17 +194,17 @@ "join-the-REDDIT-community": "Unisciti alla comunità di REDDIT", "sign-up-to-receive-our-updates": "Iscriviti per ricevere i nostri aggiornamenti", "simplex-unique-overlay-card-3-p-4": "Non ci sono identificatori o testi cifrati in comune tra il traffico del server inviato e quello ricevuto — se qualcuno lo osserva, non può determinare facilmente chi comunica con chi, anche se il TLS è compromesso.", - "simplex-unique-overlay-card-4-p-2": "La rete di SimpleX usa un protocollo aperto e fornisce un SDK per creare chat bot, consentendo l'implementazione di servizi con cui gli utenti possono interagire tramite le app SimpleX Chat — siamo impazienti di vedere quali servizi SimpleX creerai.", + "simplex-unique-overlay-card-4-p-2": "La rete di SimpleX usa un protocollo aperto e fornisce un SDK per creare chat bot, consentendo l'implementazione di servizi con cui gli utenti possono interagire tramite le app SimpleX Chat — siamo impazienti di vedere quali servizi SimpleX creerai.", "simplex-unique-card-4-p-1": "La rete SimpleX è completamente decentralizzata e indipendente da qualsiasi criptovaluta o altra rete, ad eccezione di internet.", "donate-here-to-help-us": "Dona qui per aiutarci", "to-make-a-connection": "Per stabilire una connessione:", "scan-the-qr-code-with-the-simplex-chat-app-description": "Le chiavi pubbliche e l'indirizzo della coda di messaggi in questo link NON vengono inviati in rete quando vedi questa pagina —
sono contenuti nel frammento hash dell'URL del link.", "copy-the-command-below-text": "copia il comando qui sotto e usalo nella chat:", - "privacy-matters-section-header": "Perché la privacy è importante", - "simplex-private-section-header": "Cosa rende SimpleX privato", + "privacy-matters-section-header": "Perché la privacy è importante", + "simplex-private-section-header": "Cosa rende SimpleX privato", "tap-to-close": "Tocca per chiudere", "simplex-network-1-desc": "tutti i messaggi vengono inviati tramite i server, garantendo una migliore privacy dei metadati e una consegna asincrona dei messaggi affidabile, evitando molti", - "simplex-network-3-desc": "i server forniscono code unidirezionali per connettere gli utenti, ma non hanno visibilità del grafo delle connessioni di rete — solo gli utenti.", + "simplex-network-3-desc": "i server forniscono code unidirezionali per connettere gli utenti, ma non hanno visibilità del grafo delle connessioni di rete — solo gli utenti.", "comparison-point-5-text": "Componente centrale o altro attacco a livello di rete", "comparison-section-list-point-5": "Non protegge la privacy dei metadati degli utenti", "comparison-section-list-point-3": "Chiave pubblica o altro ID univoco globale", @@ -233,7 +233,7 @@ "docs-dropdown-7": "Traduci SimpleX Chat", "glossary": "Glossario", "releases-to-this-repo-are-done-1-2-days-later": "Le pubblicazioni su questo repo avvengono diversi giorni dopo", - "f-droid-page-f-droid-org-repo-section-text": "I repository di SimpleX Chat e F-Droid.org firmano i pacchetti con chiavi diverse. Per passare da uno all'altro, esporta il database della chat e reinstalla l'app.", + "f-droid-page-f-droid-org-repo-section-text": "I repository di SimpleX Chat e F-Droid.org firmano i pacchetti con chiavi diverse. Per passare da uno all'altro, esporta il database della chat e reinstalla l'app.", "signing-key-fingerprint": "Impronta della chiave di firma (SHA-256)", "f-droid-org-repo": "Repo di F-Droid.org", "stable-versions-built-by-f-droid-org": "Versioni stabili compilate da F-Droid.org", @@ -241,7 +241,7 @@ "simplex-chat-via-f-droid": "SimpleX Chat via F-Droid", "simplex-chat-repo": "Repo di SimpleX Chat", "stable-and-beta-versions-built-by-developers": "Versioni stabili e beta compilate dagli sviluppatori", - "f-droid-page-simplex-chat-repo-section-text": "Per aggiungerlo al tuo client F-Droid scansiona il codice QR o usa questo URL:", + "f-droid-page-simplex-chat-repo-section-text": "Per aggiungerlo al tuo client F-Droid scansiona il codice QR o usa questo URL:", "comparison-section-list-point-4a": "I relay di SimpleX non possono compromettere la crittografia e2e. Verifica il codice di sicurezza per mitigare gli attacchi sul canale fuori banda", "hero-overlay-3-title": "Valutazioni della sicurezza", "hero-overlay-card-3-p-2": "Trail of Bits ha revisionato i componenti di crittografia e di rete della piattaforma SimpleX nel novembre 2022. Maggiori informazioni.", @@ -262,29 +262,29 @@ "index-hero-p1": "La prima rete in cui possiedi la tua identità, i contatti e i gruppi.", "index-hero-download-desktop-btn-title": "Scarica l'app desktop di SimpleX", "index-testflight-title": "Anteprima pubblica per iOS su TestFlight", - "index-f-droid-title": "Repository F-Droid di SimpleX", - "index-security-assessment-title": "valutazione della sicurezza", - "index-security-review-2022-title": "Revisione della sicurezza 2022", - "index-security-review-2024-title": "Revisione della sicurezza 2024", - "index-hero-h1": "Sii
libero", + "index-f-droid-title": "SimpleX via F-Droid", + "index-security-assessment-title": "Analisi della sicurezza", + "index-security-review-2022-title": "Analisi della sicurezza 2022", + "index-security-review-2024-title": "Analisi della sicurezza 2024", + "index-hero-h1": "Vivi
libero", "index-security-audits-label": "Analisi della
sicurezza", - "index-publications-privacy-guides-title": "consigli sulla messaggistica", + "index-publications-privacy-guides-title": "Consigli sulla messaggistica di Privacy Guides", "index-publications-whonix-title": "consigli sulla messaggistica di Whonix", - "index-publications-heise-title": "pubblicazione", - "index-publications-kuketz-title": "revisione", - "index-publications-optout-title": "intervista podcast", + "index-publications-heise-title": "Pubblicazioni di Heise Online", + "index-publications-kuketz-title": "Recensione di Mike Kuketz", + "index-publications-optout-title": "Intervista podcast di OptOut", "worlds-most-secure-messaging": "La messaggistica più sicuro del mondo", "index-messaging-p1": "SimpleX usa una crittografia end-to-end all'avanguardia.", - "index-messaging-p2": "Per la tua sicurezza e privacy, i server non possono vedere i messaggi o con chi parli.", + "index-messaging-p2": "Per la tua sicurezza e privacy, i server non possono vedere i messaggi e con chi parli.", "index-messaging-cta": "Scopri di più sui messaggi di SimpleX", "index-nextweb-h2": "Il nuovo web
è tuo", - "index-nextweb-p1": "SimpleX è fondato sulla convinzione che devi possedere la tua identità, i tuoi contatti e le tue comunità.", + "index-nextweb-p1": "SimpleX è fondato sulla convinzione che devi possedere la tua identità, i tuoi contatti e le tue comunità.", "index-nextweb-p2": "La rete aperta e decentralizzata consente di connetterti con persone e condividere idee: sii libero e al sicuro.", - "index-token-h2": "Comunità che durano", + "index-token-h2": "Comunità fatte per restare", "index-token-p1": "Sosterrai i tuoi gruppi preferiti con futuri buoni comunitari.", "index-token-p2": "I buoni pagheranno i server, per consentire alle tue comunità di rimanere libere e indipendenti.", - "index-token-cta": "Scopri di più e ricevi un NFT gratuito
per uno sconto del 20% su SimpleX Token!", - "index-roadmap-h2": "Tabella di marcia di SimpleX per un internet libero", + "index-token-cta": "Scopri di più e ricevi un NFT gratuito
per provarlo in anticipo.", + "index-roadmap-h2": "Tabella di marcia per un internet libero", "index-roadmap-2025": "2025", "index-roadmap-2025-title": "Scalabilità per comunità numerose", "index-roadmap-2025-desc": "Fuga da piattaforme centralizzate", @@ -294,12 +294,12 @@ "index-roadmap-2027": "2027", "index-roadmap-2027-title": "Fare crescere le tue comunità", "index-roadmap-2027-desc": "Strumenti per promuovere le tue comunità", - "index-directory-h2": "Entra nelle comunità di SimpleX", + "index-directory-h2": "Unisciti alle comunità di SimpleX", "index-directory-p1": "Centinaia di migliaia di persone si fidano già di SimpleX.", "index-directory-p2": "Trova le comunità nella directory di SimpleX e creane una tua!", "index-directory-cta": "Vedi la directory di SimpleX", "index-directory-users-group-title": "Gruppo utenti SimpleX", - "how-secure-comparison-title": "Quanto è sicura la crittografia end-to-end in diverse app di messaggistica?", + "how-secure-comparison-title": "Confronto della sicurezza di crittografia end-to-end in diverse app di messaggistica", "how-secure-message-padding": "Riempimento dei messaggi", "how-secure-repudiation-deniability": "Ripudio (negabilità)", "how-secure-forward-secrecy": "Forward secrecy", diff --git a/website/langs/ja.json b/website/langs/ja.json index 1f71aad624..31ef2669ef 100644 --- a/website/langs/ja.json +++ b/website/langs/ja.json @@ -52,7 +52,7 @@ "chat-protocol": "チャットプロトコル", "chat-bot-example": "チャットボットの例", "donate": "寄付", - "copyright-label": "© 2020-2025 SimpleX | Open-Source Project", + "copyright-label": "© 2020-2025 SimpleX Chat | Open-Source Project", "hero-p-1": "他のアプリにはユーザー ID があります: Signal、Matrix、Session、Briar、Jami、Cwtch など。
SimpleX にはありません。乱数さえもありません
これにより、プライバシーが大幅に向上します。", "copy-the-command-below-text": "以下のコマンドをコピーしてチャットで使用します:", "simplex-private-card-9-point-1": "各メッセージ キューは、異なる送信アドレスと受信アドレスを使用してメッセージを一方向に渡します。", diff --git a/website/langs/nl.json b/website/langs/nl.json index 1cdffc6ae5..f8297e2841 100644 --- a/website/langs/nl.json +++ b/website/langs/nl.json @@ -17,7 +17,7 @@ "chat-bot-example": "Chatbot voorbeeld", "smp-protocol": "SMP protocol", "donate": "Doneer", - "copyright-label": "© 2020-2025 SimpleX | Open-sourceproject", + "copyright-label": "© 2020-2025 SimpleX Chat | Open-sourceproject", "simplex-chat-protocol": "SimpleX Chat protocol", "terminal-cli": "Terminal CLI", "terms-and-privacy-policy": "Privacybeleid", diff --git a/website/langs/pl.json b/website/langs/pl.json index e3c8fa6059..54a552685f 100644 --- a/website/langs/pl.json +++ b/website/langs/pl.json @@ -15,7 +15,7 @@ "smp-protocol": "Protokół SMP", "chat-protocol": "Protokół czatu", "donate": "Darowizna", - "copyright-label": "© 2020-2025 SimpleX | Projekt Open-Source", + "copyright-label": "© 2020-2025 SimpleX Chat | Projekt Open-Source", "simplex-chat-protocol": "Protokół SimpleX Chat", "terminal-cli": "Terminal CLI", "terms-and-privacy-policy": "Polityka prywatności", diff --git a/website/langs/pt_BR.json b/website/langs/pt_BR.json index b1a1f2decd..3b7cc23703 100644 --- a/website/langs/pt_BR.json +++ b/website/langs/pt_BR.json @@ -25,7 +25,7 @@ "smp-protocol": "Protocolo SMP", "chat-protocol": "Protocolo de bate-papo", "donate": "Doar", - "copyright-label": "© 2020-2025 SimpleX | Projeto de Código Livre", + "copyright-label": "© 2020-2025 SimpleX Chat | Projeto de Código Livre", "simplex-chat-protocol": "Protocolo Chat SimpleX", "terminal-cli": "CLI Terminal", "hero-header": "Privacidade redefinida", diff --git a/website/langs/ro.json b/website/langs/ro.json index d129c1a7d1..cfe489967f 100644 --- a/website/langs/ro.json +++ b/website/langs/ro.json @@ -20,7 +20,7 @@ "smp-protocol": "Protocolul SMP", "chat-protocol": "Protocol de chat", "donate": "Donează", - "copyright-label": "© 2020-2025 SimpleX | Proiect Open-Source", + "copyright-label": "© 2020-2025 SimpleX Chat | Proiect Open-Source", "simplex-chat-protocol": "Protocolul SimpleX Chat", "terminal-cli": "Terminal CLI", "terms-and-privacy-policy": "Politică de confidențialitate", diff --git a/website/langs/ru.json b/website/langs/ru.json index 6ab4827496..5c59c3e848 100644 --- a/website/langs/ru.json +++ b/website/langs/ru.json @@ -1,6 +1,6 @@ { "copy-the-command-below-text": "скопируйте приведенную ниже команду и используйте ее в чате:", - "copyright-label": "© 2020-2025 SimpleX | Проект с открытым исходным кодом", + "copyright-label": "© 2020-2025 SimpleX Chat | Проект с открытым исходным кодом", "chat-bot-example": "Пример Чат бота", "simplex-private-card-9-point-1": "Каждая очередь сообщений передает сообщения в одном направлении с разными адресами отправки и получения.", "simplex-private-card-1-point-2": "NaCL cryptobox в каждой очереди для предотвращения корреляции трафика между очередями сообщений, в случае компрометированного TLS.", @@ -283,7 +283,7 @@ "index-token-h2": "Стабильные Сообщества", "index-token-p1": "Вы сможете поддерживать Ваши любимые группы с помощью будущих Ваучеров Групп.", "index-token-p2": "Ваучеры будут использоваться для оплаты за серверы, чтобы группы оставались свободными и независимыми.", - "index-token-cta": "Узнайте больше и возьмите Ваш бесплатный NFT, чтобы получить скидку 20% на SimpleX токены!", + "index-token-cta": "Узнайте больше и возьмите бесплатный NFT, чтобы участвовать в тестировании.", "index-roadmap-h2": "Путь Сети SimpleX к Свободному Интернету", "index-roadmap-2025": "2025", "index-roadmap-2025-title": "Большие каналы и группы", diff --git a/website/langs/tr.json b/website/langs/tr.json index c6f6d24c93..ad8914fa18 100644 --- a/website/langs/tr.json +++ b/website/langs/tr.json @@ -21,7 +21,7 @@ "smp-protocol": "SMP Protokolü", "chat-protocol": "Sohbet Protokolü", "donate": "Bağış Yap", - "copyright-label": "© 2020-2025 SimpleX | Açık Kaynak Projesi", + "copyright-label": "© 2020-2025 SimpleX Chat | Açık Kaynak Projesi", "simplex-chat-protocol": "SimpleX Sohbet Protokolü", "terminal-cli": "Terminal Komut Satırı Arayüzü", "terms-and-privacy-policy": "Gizlilik Politikası", diff --git a/website/langs/uk.json b/website/langs/uk.json index 1c1780edb3..c2b02d7cf2 100644 --- a/website/langs/uk.json +++ b/website/langs/uk.json @@ -78,7 +78,7 @@ "smp-protocol": "Протокол SMP", "chat-protocol": "Протокол чату", "donate": "Пожертвувати", - "copyright-label": "© 2020-2025 SimpleX | Проект з відкритим кодом", + "copyright-label": "© 2020-2025 SimpleX Chat | Проект з відкритим кодом", "simplex-chat-protocol": "Протокол чату SimpleX", "terminal-cli": "Термінал CLI", "hero-header": "Приватність переосмислена", diff --git a/website/langs/zh_Hans.json b/website/langs/zh_Hans.json index 6dbd261864..6ac8bdf833 100644 --- a/website/langs/zh_Hans.json +++ b/website/langs/zh_Hans.json @@ -50,14 +50,14 @@ "simplex-explained-tab-3-text": "3. 服务器能看到什么", "hero-header": "重新定义隐私", "simplex-explained-tab-1-p-2": "它是如何利用单向消息队列并不利用用户识别符工作的?", - "simplex-explained-tab-2-p-2": "服务器只会单向传输消息,而无法掌握用户的对话或连接的全貌。", + "simplex-explained-tab-2-p-2": "服务器只单向传输消息,无法掌握用户的对话或连接的全貌。", "simplex-explained-tab-3-p-1": "服务器对每个队列都有单独的匿名凭证,并且不知道这些凭证属于哪些用户。", "donate": "捐赠", "simplex-explained-tab-2-p-1": "对于每个连接,您都会使用两个单独的消息队列,通过不同的服务器发送和接收消息。", "simplex-chat-protocol": "SimpleX 聊天协议", "smp-protocol": "SMP协议", "chat-protocol": "聊天协议", - "copyright-label": "© 2020-2025 SimpleX | 开源项目", + "copyright-label": "© 2020-2025 SimpleX Chat | 开源项目", "terminal-cli": "命令行程式", "simplex-explained-tab-1-p-1": "您可以创建联系人和群组,并进行双向对话,就像是任何其他即时通讯软件一样。", "hero-p-1": "其他应用——如Signal、Matrix、Session、Briar、Jami、Cwtch 等——都需要用户 ID。
而SimpleX 不需要用户ID,连随机生成的也不需要。
这从根本上改善了您的隐私。", @@ -108,34 +108,34 @@ "hero-overlay-card-1-p-3": "您定义用于接收消息的服务器、您的联系人—— 您用来向他们发送消息的服务器。 每个会话都很可能会使用两个不同的服务器。", "hero-overlay-card-1-p-4": "此设计可防止在应用程序级别泄漏任何用户元数据。 为了进一步改善隐私并保护您的 IP 地址,您可以通过 Tor 连接到消息服务器。", "hero-overlay-card-1-p-5": "只有客户端设备存储用户配置文件、联系人和群组; 消息均使用两层端到端加密发送。", - "simplex-network-overlay-card-1-p-1": "P2P 消息传递协议和应用程序存在各种问题,使得它们不如 SimpleX 可靠,分析起来更复杂,并且 容易受到多种类型的攻击。", - "hero-overlay-card-1-p-6": "在 SimpleX 白皮书 中阅读更多内容。", + "simplex-network-overlay-card-1-p-1": "P2P 消息传递协议和应用程序存在各种问题,使得它们不如 SimpleX 可靠,分析起来更复杂,并且 容易受到多种类型的攻击。", + "hero-overlay-card-1-p-6": "阅读 SimpleX 白皮书了解更多信息。", "hero-overlay-card-2-p-2": "然后他们可以将这些信息与现有的公共社交网络相关联,并确定一些真实身份。", "hero-overlay-card-2-p-1": "当用户具有持久身份时,即使这只是一个随机数(如会话 ID),提供商或攻击者也有可能观察到用户如何连接以及他们发送了多少消息。", "hero-overlay-card-2-p-3": "即使是使用 Tor v3 服务的最私密的应用程序,如果您通过同一个人资料与两个不同的联系人交谈,他们也可以证明他们与同一个人有联系。", "hero-overlay-card-2-p-4": "为防止这些攻击,SimpleX 在其设计中不包含任何用户 ID 。 而且,如果您使用隐身模式,您将为每个联系人显示不同的名称,避免他们之间共享任何数据。", - "simplex-network-overlay-card-1-li-1": "P2P 网络依赖于 分布式散列表(DHT) 的某些变体来路由消息。 DHT 在设计上必须平衡可达性和延迟。 SimpleX 比 P2P 具有更好的可达性和更低的延迟,因为消息可以通过通讯双方选择的多个服务器并行地冗余传递。若是在 P2P 网络中,消息则需要使用算法选择,并依次通过 O(log N) 个节点。", - "simplex-network-overlay-card-1-li-4": "P2P 实现(如 BitTorrent)可能会被某些互联网提供商阻止。 SimpleX 与传输协议无关 — 它可以在标准网络协议上工作,例如 WebSockets。", - "simplex-network-overlay-card-1-li-3": "P2P 并未解决中间人攻击(MITM Attack) 问题。大多数现有的 P2P 实现没有使用带外通讯来进行初始密钥的交换,而 SimpleX 使用带外通讯,或者在某些情况下,使用预先存在的安全和可信连接来进行初始密钥交换。", - "simplex-network-overlay-card-1-li-5": "所有已知的 P2P 网络都可能受到 Sybil 攻击,因为每个节点都是可发现的,并且网络作为一个整体运行。 已知的缓解措施不是需要一个中心化的组件就是需要昂贵的工作量证明。而 SimpleX 网络没有服务器可发现性,它是碎片化的并且作为多个隔离的子网运行,这样全网络范围的攻击便无从实现。", - "simplex-network-overlay-card-1-li-6": "P2P 网络可能受到 DRDoS 攻击 。客户端有能力重新广播和放大流量,从而导致整个网络范围内的服务中断。 SimpleX 客户端仅中继来自已知连接的流量,因此不能被攻击者用来放大整个网络的流量。", + "simplex-network-overlay-card-1-li-1": "P2P 网络依赖于分布式散列表(DHT) 的某些变体来路由消息。 DHT 在设计上必须平衡可达性和延迟。 SimpleX 比 P2P 具有更好的可达性和更低的延迟,因为消息可以通过通讯双方选择的多个服务器并行地冗余传递。若是在 P2P 网络中,消息则需要使用算法选择,并依次通过 O(log N) 个节点。", + "simplex-network-overlay-card-1-li-4": "P2P 实现(如BitTorrent)可能会被某些互联网提供商阻止。 SimpleX 与传输协议无关 — 它可以在标准网络协议上工作,例如 WebSockets。", + "simplex-network-overlay-card-1-li-3": "P2P 并未解决中间人攻击(MITM Attack) 问题。大多数现有的 P2P 实现没有使用带外通讯来进行初始密钥的交换,而 SimpleX 使用带外通讯,或者在某些情况下,使用预先存在的安全和可信连接来进行初始密钥交换。", + "simplex-network-overlay-card-1-li-5": "所有已知的 P2P 网络都可能受到Sybil 攻击,因为每个节点都是可发现的,并且网络作为一个整体运行。 已知的缓解措施不是需要一个中心化的组件就是需要昂贵的工作量证明。而 SimpleX 网络没有服务器可发现性,它是碎片化的并且作为多个隔离的子网运行,这样全网络范围的攻击便无从实现。", + "simplex-network-overlay-card-1-li-6": "P2P 网络可能受到 分布式反射拒绝服务攻击 。客户端有能力重新广播和放大流量,从而导致整个网络范围内的服务中断。 SimpleX 客户端仅中继来自已知连接的流量,因此不能被攻击者用来放大整个网络的流量。", "privacy-matters-overlay-card-1-p-2": "在线零售商知道收入较低的人更有可能在紧急情况下购买商品,因此他们可能会收取更高的价格或取消折扣。", - "privacy-matters-overlay-card-1-p-3": "一些金融和保险公司使用社交图谱来确定利率和保费。 它通常会让收入较低的人支付更多—— 它被称为“贫困溢价”。", + "privacy-matters-overlay-card-1-p-3": "一些金融和保险公司使用社交图谱来确定利率和保费。 它通常会让收入较低的人支付更多—它被称为“贫困溢价”。", "privacy-matters-overlay-card-1-p-4": "SimpleX 网络比任何替代方案都能更好地保护您人际关系层面的隐私,防止您的社交图谱被任何公司或组织使用。 即使人们使用 SimpleX Chat 应用预配置的服务器,服务器运营方也不知道用户数量或他们的连接数。", - "privacy-matters-overlay-card-2-p-1": "不久前,我们观察到几次大选被一家知名咨询公司操纵,该公司使用我们的社交图谱扭曲我们对现实世界的看法并操纵我们的选票。", + "privacy-matters-overlay-card-2-p-1": "不久前,我们观察到几次大选被一家知名咨询公司操纵,该公司使用我们的社交图谱扭曲我们对现实世界的看法并操纵我们的选票。", "privacy-matters-overlay-card-2-p-2": "为了客观并做出独立的决定,您需要控制您的信息空间。 而这只有当您使用没有能力访问您的社交图谱的,注重隐私的通信网络时,这才有可能。", "privacy-matters-overlay-card-2-p-3": "SimpleX 是第一个没有设计任何用户标识符的网络,这样能比任何已知的替代方案都更好地保护您的连接图谱。", - "privacy-matters-overlay-card-3-p-2": "最令人震惊的故事之一是 Mohamedou Ould Salahi 在他的回忆录中描述并在毛里塔尼亚电影中展示的经历。 他在未经审判的情况下被关进关塔那摩集中营,并在打电话给他在阿富汗的亲戚后在那里遭受了 15 年的折磨,他被怀疑参与了 9/11 袭击,尽管他在过去 10 年住在德国。", + "privacy-matters-overlay-card-3-p-2": "最令人震惊的故事之一是 Mohamedou Ould Salahi 在他的回忆录中描述并在毛里塔尼亚电影中展示的经历。 他在未经审判的情况下被关进关塔那摩集中营,并在打电话给他在阿富汗的亲戚后在那里遭受了 15 年的折磨,他被怀疑参与了 9/11 袭击,尽管他在过去 10 年住在德国。", "privacy-matters-overlay-card-3-p-1": "每个人都应该关心他们通信的隐私和安全——无害的谈话会让您处于危险之中,即使您没有什么可隐瞒的。", - "privacy-matters-overlay-card-3-p-3": "普通人会因为他们在网上分享的内容而被捕,即使是通过他们的“匿名”帐户,即使是在民主国家。", + "privacy-matters-overlay-card-3-p-3": "普通人会因为他们在网上分享的内容而被捕,即使是通过他们的“匿名”账户,即使是在民主国家。", "privacy-matters-overlay-card-3-p-4": "使用端到端加密的即时通讯软件还不够,我们都应该使用保护我们个人网络隐私——即我们与谁有联系的即时通讯软件。", "simplex-unique-overlay-card-1-p-1": "与其他消息网络不同,SimpleX 没有分配给用户的标识符。 它不依赖电话号码、基于域的地址(如电子邮件或 XMPP)、用户名、公钥甚至随机数来识别其用户—— SimpleX 服务器运营方不知道有多少人使用其服务器。", - "simplex-unique-overlay-card-1-p-2": "为了传递消息,SimpleX 使用单向消息队列的成对匿名地址,通常通过不同的服务器将接收和发送的消息分开。", + "simplex-unique-overlay-card-1-p-2": "为了传递消息,SimpleX 使用单向消息队列的成对匿名地址,通常通过不同的服务器将接收和发送的消息分开。", "simplex-unique-overlay-card-1-p-3": "这种设计保护了您正在与之通信的人的隐私,将其隐藏在 SimpleX 网络的服务器和任何观察者之外。 要对服务器隐藏您的 IP 地址,您可以通过 Tor 连接到 SimpleX 服务器。", "simplex-unique-overlay-card-4-p-1": "您可以将 SimpleX 与您自己的服务器一起使用,并且仍然可以与使用应用中预配置服务器的人们进行通信。", "simplex-unique-card-3-p-2": "端到端加密的消息在被收到前会暂时保存在 SimpleX 中继服务器上,传送完成后它们会被永久删除。", "simplex-unique-card-1-p-1": "SimpleX 保护您的个人资料、联系人和元数据的隐私,不让 SimpleX 网络服务器和任何观察者看到它们。", - "simplex-unique-overlay-card-4-p-3": "如果您正在考虑为在SimpleX 网络上开发,例如,为 SimpleX 应用程序用户开发聊天机器人,或将 SimpleX 聊天库集成到您的移动应用程序中,请 联系我们 以获取建议和支持。", + "simplex-unique-overlay-card-4-p-3": "如果您正在考虑为在SimpleX 网络上开发,例如,为 SimpleX 应用程序用户开发聊天机器人,或将 SimpleX 聊天库集成到您的移动应用,请 联系我们获取建议和支持。", "simplex-unique-card-4-p-2": "您可以搭配自己的服务器来使用 SimpleX 或使用我们提供的服务器 — 并仍然连接到任何用户。", "simplex-unique-card-2-p-1": "因为您在 SimpleX 网络上没有标识符或固定地址,所以除非您以二维码或链接的形式分享一次性或临时用户地址,没有人可以联系您。", "simplex-unique-card-3-p-1": "SimpleX 以便携式加密数据库格式将所有用户数据存储在客户端设备上—— 它可以转移到另一个设备。", @@ -143,7 +143,7 @@ "we-invite-you-to-join-the-conversation": "我们邀请您加入对话", "enter-your-email-address": "输入您的电子邮箱地址", "join-the-REDDIT-community": "加入 REDDIT 社区", - "why-simplex-is-unique": "为什么 SimpleX 是 独特的", + "why-simplex-is-unique": "为什么 SimpleX 是独特的", "learn-more": "了解更多", "join-us-on-GitHub": "在 GitHub 上加入我们", "more-info": "更多信息", @@ -157,30 +157,30 @@ "see-simplex-chat": "查看 SimpleX 聊天", "install-simplex-app": "安装 SimpleX 应用程序", "connect-in-app": "在应用程序中连接", - "tap-the-connect-button-in-the-app": "点按应用中的 “连接” 按钮", + "tap-the-connect-button-in-the-app": "点按应用中的 “连接” 按钮", "scan-the-qr-code-with-the-simplex-chat-app": "使用 SimpleX Chat 应用程序扫描二维码", "scan-the-qr-code-with-the-simplex-chat-app-description": "当您查看此页面时,此链接中的公钥和消息队列地址不会通过网络发送 ——它们包含在链接 URL 的哈希片段中。", "installing-simplex-chat-to-terminal": "将 SimpleX 聊天安装到终端", "github-repository": "GitHub 仓库", "copy-the-command-below-text": "复制下面的命令并在聊天中使用它:", - "privacy-matters-section-header": "为什么隐私很重要", + "privacy-matters-section-header": "为什么隐私很重要", "the-instructions--source-code": "如何下载或从源代码下载进行编译的说明。", "if-you-already-installed-simplex-chat-for-the-terminal": "如果您已经为终端安装了 SimpleX Chat", "if-you-already-installed": "如果您已经安装", "simplex-chat-for-the-terminal": "用于终端的 SimpleX Chat", "privacy-matters-section-label": "确保您的即时通讯软件无法访问您的数据!", - "privacy-matters-section-subheader": "保护您的元数据的隐私—— 您与谁交谈 — 保护您免受:", - "simplex-private-section-header": "是什么让 SimpleX 能够保密", + "privacy-matters-section-subheader": "保护您的元数据的隐私— 您与谁交谈 — 保护您免受:", + "simplex-private-section-header": "是什么让 SimpleX 能够保密", "simplex-network-2-desc": "SimpleX 中继服务器不存储用户配置文件、联系人和传递的消息,不相互连接,并且没有服务器目录。", "tap-to-close": "点击关闭", - "simplex-network-section-header": "SimpleX 网络", + "simplex-network-section-header": "SimpleX 网络", "simplex-network-section-desc": "Simplex Chat 通过结合 P2P 和联邦网络的优势使其保密性无与伦比。", "no-private": "否 - 私密", "simplex-network-1-desc": "所有消息都通过服务器发送,既能更好地保护元数据隐私和可靠地传递异步消息,同时也能避免许多", "simplex-network-3-header": "SimpleX 网络", "protocol-1-text": "Signal、其他大平台", "comparison-point-4-text": "单一或中心化网络", - "simplex-network-3-desc": "服务器提供单向队列来连接用户,但是他们看不到网络连接图图谱— 只有用户可以。", + "simplex-network-3-desc": "服务器提供单向队列来连接用户,但是他们看不到网络连接图图谱— 只有用户可以。", "comparison-section-header": "与其他协议的比较", "protocol-3-text": "P2P协议", "no": "否", @@ -190,7 +190,7 @@ "comparison-point-2-text": "中间人攻击的可能性", "comparison-point-5-text": "中央组件或其他全网攻击", "yes": "是", - "comparison-section-list-point-5": "不保护用户的元数据", + "comparison-section-list-point-5": "不保护用户的元数据隐私", "no-resilient": "不依赖 - 有韧性", "no-decentralized": "不依赖 - 去中心化的", "comparison-section-list-point-3": "公钥或其他一些全球唯一的 ID", @@ -201,7 +201,7 @@ "see-here": "参见此处", "comparison-section-list-point-7": "P2P 网络要么拥有中央权威,要么整个网络可能被攻陷", "simplex-private-card-5-point-1": "SimpleX 为每个加密层进行内容填充来对抗长度扩展攻击。", - "simplex-unique-overlay-card-4-p-2": "SimpleX 网络使用开放协议并提供用于创建聊天机器人的 SDK, 允许用户实现通过 SimpleX Chat 应用程序与之交互的服务—— 我们真的很期待看到您会依托SimpleX构建哪些服务。", + "simplex-unique-overlay-card-4-p-2": "SimpleX 网络使用开放协议并提供用于创建聊天机器人的 SDK, 允许用户实现通过 SimpleX Chat 应用程序与之交互的服务—我们真的很期待看到您会依托SimpleX构建哪些服务。", "simplex-unique-overlay-card-2-p-2": "即使使用可选的用户地址,当它被用于发送垃圾邮件联系请求,您可以更改或完全删除它而不会丢失任何连接。", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat 使用便携式加密数据库格式仅将所有用户数据存储在客户端设备上,该格式可以导出并传输到任何支持的设备。", "donate-here-to-help-us": "在这里捐款来帮助我们", @@ -239,10 +239,10 @@ "stable-versions-built-by-f-droid-org": "由 F-Droid.org 构建的稳定版本", "simplex-chat-repo": "SimpleX 存储库", "stable-and-beta-versions-built-by-developers": "开发人员构建的稳定版和测试版", - "f-droid-page-simplex-chat-repo-section-text": "要将其添加到您的 F-Droid 客户端,请扫描二维码或使用以下 URL:", + "f-droid-page-simplex-chat-repo-section-text": "要将其添加到您的 F-Droid 客户端,请扫描二维码或使用以下 URL:", "comparison-section-list-point-4a": "SimpleX 中继无法破坏 e2e 加密。 验证安全代码以减轻对带外通道的攻击", "docs-dropdown-8": "SimpleX 目录", - "f-droid-page-f-droid-org-repo-section-text": "SimpleX Chat 和 F-Droid.org 存储库使用不同的密钥对构建进行签名。 如需切换,请导出聊天数据库并重新安装应用。", + "f-droid-page-f-droid-org-repo-section-text": "SimpleX Chat 和 F-Droid.org 存储库使用不同的密钥对构建进行签名。 如需切换,请导出聊天数据库并重新安装应用。", "hero-overlay-3-title": "安全性评估", "hero-overlay-card-3-p-2": "2022年11月份,Trail of Bits 审核了 SimpleX 平台的密码学和网络部件。更多内容见 该公告。", "jobs": "加入团队", diff --git a/website/langs/zh_Hant.json b/website/langs/zh_Hant.json index 1896fa3478..88dbdf107b 100644 --- a/website/langs/zh_Hant.json +++ b/website/langs/zh_Hant.json @@ -19,7 +19,7 @@ "simplex-explained-tab-2-p-2": "伺服器僅單向傳遞消息,無法全面瞭解使用者的對話記錄或連接。", "simplex-explained-tab-2-p-1": "對於每個連接,您可以使用兩個單獨的消息佇列通過不同的伺服器發送和接收消息。", "chat-protocol": "聊天協定", - "copyright-label": "© 2020-2025 SimpleX |開源專案", + "copyright-label": "© 2020-2025 SimpleX Chat |開源專案", "donate": "捐助", "simplex-explained-tab-1-p-1": "你可以建立聯絡人和群組,並進行雙向對話,就像在任何其他即時通訊軟件中一樣。", "simplex-explained-tab-1-p-2": "它如何在沒有使用者個人檔案識別符的情況下使用單向佇列?", diff --git a/website/package.json b/website/package.json index bbe3a17a68..4192563164 100644 --- a/website/package.json +++ b/website/package.json @@ -20,6 +20,7 @@ "@11ty/eleventy-plugin-rss": "^1.2.0", "@simplex-chat/webrtc": "^0.1.1", "common-tags": "^1.8.2", + "ethers": "^6.15.0", "fast-uri": "^2.1.0", "markdown-it-anchor": "^8.6.4", "markdown-it-replace-link": "^1.1.0", diff --git a/website/src/_data/docs_dropdown.json b/website/src/_data/docs_dropdown.json index f97c2aeff2..94bc69f8f3 100644 --- a/website/src/_data/docs_dropdown.json +++ b/website/src/_data/docs_dropdown.json @@ -13,8 +13,8 @@ "url": "/docs/directory.html" }, { - "title": "docs-dropdown-3", - "url": "/docs/sql.html" + "title": "docs-dropdown-2", + "url": "/docs/android.html" }, { "title": "docs-dropdown-4", diff --git a/website/src/_data/glossary.json b/website/src/_data/glossary.json index a16c2b9541..155b732c52 100644 --- a/website/src/_data/glossary.json +++ b/website/src/_data/glossary.json @@ -103,6 +103,10 @@ "term": "Post-quantum cryptography", "definition": "Post-quantum cryptography" }, + { + "term": "Post-quantum hybrid crypto", + "definition": "Post-quantum cryptography" + }, { "term": "Proxied peer-to-peer", "definition": "Proxied peer-to-peer" diff --git a/website/src/_data/languages.json b/website/src/_data/languages.json index 340cc59da9..0736597953 100644 --- a/website/src/_data/languages.json +++ b/website/src/_data/languages.json @@ -4,7 +4,8 @@ "label": "en", "name": "English", "flag": "/img/flags/en.svg", - "enabled": true + "enabled": true, + "home": true }, { "label": "ar", @@ -25,13 +26,15 @@ "label": "de", "name": "Deutsch", "flag": "/img/flags/de.svg", - "enabled": true + "enabled": true, + "home": true }, { "label": "es", "name": "Español", "flag": "/img/flags/es.svg", - "enabled": true + "enabled": true, + "home": true }, { "label": "fi", @@ -56,13 +59,22 @@ "label": "hu", "name": "Magyar", "flag": "/img/flags/hu.svg", - "enabled": true + "enabled": true, + "home": true + }, + { + "label": "id", + "name": "Indonesia", + "flag": "/img/flags/id.svg", + "enabled": true, + "home": true }, { "label": "it", "name": "Italiano", "flag": "/img/flags/it.svg", - "enabled": true + "enabled": true, + "home": true }, { "label": "ja", @@ -110,7 +122,8 @@ "label": "ru", "name": "Русский", "flag": "/img/flags/ru.svg", - "enabled": true + "enabled": true, + "home": true } ] } \ No newline at end of file diff --git a/website/src/_includes/footer.html b/website/src/_includes/footer.html index 7f86200fb0..01b22d8b97 100644 --- a/website/src/_includes/footer.html +++ b/website/src/_includes/footer.html @@ -1,5 +1,5 @@ {% set lang = page.url | getlang %} -
+