diff --git a/apps/frontend/src/App.tsx b/apps/frontend/src/App.tsx index 046c43b5..5c7447fc 100644 --- a/apps/frontend/src/App.tsx +++ b/apps/frontend/src/App.tsx @@ -3,12 +3,13 @@ import { createBrowserRouter, RouterProvider, Outlet, useLocation } from "react- import { Proposal } from "./pages/Proposal/Proposal"; import { Proposals } from "./pages/Proposals"; import { CreateProposal } from "./pages/CreateProposal/CreateProposal"; +import { AdminDashboard } from "./pages/Admin/AdminDashboard"; import { Footer } from "./components/footer/Footer"; import { ScrollToTop } from "./components/ui/ScrollToTop"; import { useVoteCastInvalidate } from "./hooks/useVoteCastInvalidate"; import { useProposalCreatedInvalidate } from "./hooks/useProposalCreatedInvalidate"; -const excludedRoutes = ["/create-proposal"]; +const excludedRoutes = ["/create-proposal", "/admin"]; function Layout() { const { pathname } = useLocation(); @@ -46,6 +47,10 @@ const router = createBrowserRouter([ path: "create-proposal", element: , }, + { + path: "admin", + element: , + }, ], }, ]); diff --git a/apps/frontend/src/components/ui/InputMessage.tsx b/apps/frontend/src/components/ui/InputMessage.tsx index 45d6f9d1..57f26104 100644 --- a/apps/frontend/src/components/ui/InputMessage.tsx +++ b/apps/frontend/src/components/ui/InputMessage.tsx @@ -15,5 +15,9 @@ export const InputMessage = ({ error, message }: InputMessageProps) => { {error} ); - return {message}; + return ( + + {message} + + ); }; diff --git a/apps/frontend/src/constants.tsx b/apps/frontend/src/constants.tsx index 7c835545..657ac189 100644 --- a/apps/frontend/src/constants.tsx +++ b/apps/frontend/src/constants.tsx @@ -14,3 +14,5 @@ export const IconByVote = { [SingleChoiceEnum.FOR]: ThumbsUpIcon, [SingleChoiceEnum.ABSTAIN]: AbstainIcon, }; + +export const VALIDATOR_STAKED_VET_REQUIREMENT = BigInt("25000000000000000000000000"); // 25 million VET diff --git a/apps/frontend/src/hooks/useAdminUserRoles.ts b/apps/frontend/src/hooks/useAdminUserRoles.ts new file mode 100644 index 00000000..dd305e9f --- /dev/null +++ b/apps/frontend/src/hooks/useAdminUserRoles.ts @@ -0,0 +1,69 @@ +import { executeMultipleClauses } from "@/utils/contract"; +import { getConfig } from "@repo/config"; +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { useQuery } from "@tanstack/react-query"; +import { useCallback } from "react"; + +const ROLES = [ + "DEFAULT_ADMIN_ROLE", + "EXECUTOR_ROLE", + "SETTINGS_MANAGER_ROLE", + "NODE_WEIGHT_MANAGER_ROLE", + "UPGRADER_ROLE", + "WHITELISTED_ROLE", + "WHITELIST_ADMIN_ROLE", +] as const; + +const contractAddress = getConfig(import.meta.env.VITE_APP_ENV).vevoteContractAddress; +const contractInterface = VeVote__factory.createInterface(); + +export const useUserAdminRoles = (userAddress: string) => { + const checkUserRoles = useCallback(async () => { + try { + const roleHashMethods = ROLES.map(role => ({ + method: role, + args: [], + })); + + const roleHashResults = await executeMultipleClauses({ + contractAddress, + contractInterface, + methodsWithArgs: roleHashMethods, + }); + + const roleHashes = roleHashResults + .map((result, index) => ({ + name: ROLES[index], + hash: result?.success ? result.result.plain : null, + })) + .filter(role => role.hash); + + const hasRoleMethods = roleHashes.map(role => ({ + method: "hasRole" as const, + args: [role.hash, userAddress], + })); + + const hasRoleResults = await executeMultipleClauses({ + contractAddress, + contractInterface, + methodsWithArgs: hasRoleMethods, + }); + + const userRoles = roleHashes + .filter((_, index) => hasRoleResults[index]?.success && hasRoleResults[index].result.plain === true) + .map(role => role.name); + + return userRoles; + } catch (error) { + console.error("Error checking user roles:", error); + return []; + } + }, [userAddress]); + + return useQuery({ + queryKey: ["userRoles", userAddress], + queryFn: checkUserRoles, + enabled: Boolean(userAddress), + staleTime: 30000, + }); +}; diff --git a/apps/frontend/src/hooks/useContractRoles.ts b/apps/frontend/src/hooks/useContractRoles.ts new file mode 100644 index 00000000..88c72ff3 --- /dev/null +++ b/apps/frontend/src/hooks/useContractRoles.ts @@ -0,0 +1,85 @@ +import { executeMultipleClauses } from "@/utils/contract"; +import { getConfig } from "@repo/config"; +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { NodeManagement__factory, StargateNFT__factory } from "@vechain/vevote-contracts/typechain-types"; +import { useQuery } from "@tanstack/react-query"; +import { useCallback } from "react"; +import { useWallet } from "@vechain/vechain-kit"; +import { CONTRACT_CONFIGS, ContractType } from "@/pages/Admin/constants/contracts"; + +const getContractInterface = (contractType: ContractType) => { + switch (contractType) { + case "vevote": + return VeVote__factory.createInterface(); + case "nodeManagement": + return NodeManagement__factory.createInterface(); + case "stargate": + return StargateNFT__factory.createInterface(); + default: + return VeVote__factory.createInterface(); + } +}; + +export const useContractRoles = (contractType: ContractType = "vevote") => { + const { account } = useWallet(); + const config = CONTRACT_CONFIGS[contractType]; + const contractAddress = getConfig(import.meta.env.VITE_APP_ENV)[config.addressKey]; + const contractInterface = getContractInterface(contractType); + + const checkUserRoles = useCallback(async () => { + if (!account?.address) return []; + + try { + const roleHashMethods = config.roles.map(role => ({ + method: role, + args: [], + })); + + const roleHashResults = await executeMultipleClauses({ + contractAddress, + contractInterface, + methodsWithArgs: roleHashMethods, + }); + + const roleHashes = roleHashResults + .map((result, index) => ({ + name: config.roles[index], + hash: result?.success ? result.result.plain : null, + })) + .filter(role => role.hash); + + const hasRoleMethods = roleHashes.map(role => ({ + method: "hasRole" as const, + args: [role.hash, account.address], + })); + + const hasRoleResults = await executeMultipleClauses({ + contractAddress, + contractInterface, + methodsWithArgs: hasRoleMethods, + }); + + const userRoles = roleHashes + .filter((_, index) => hasRoleResults[index]?.success && hasRoleResults[index].result.plain === true) + .map(role => role.name); + + return userRoles; + } catch (error) { + console.error("Error checking user roles:", error); + return []; + } + }, [account?.address, contractAddress, config.roles, contractInterface]); + + const query = useQuery({ + queryKey: ["contractRoles", contractType, account?.address], + queryFn: checkUserRoles, + enabled: Boolean(account?.address), + staleTime: 30000, + }); + + return { + defaultRoles: config.roles, + roles: query.data || [], + isLoading: query.isLoading, + }; +}; diff --git a/apps/frontend/src/hooks/useFormatTime.ts b/apps/frontend/src/hooks/useFormatTime.ts new file mode 100644 index 00000000..ea1fad25 --- /dev/null +++ b/apps/frontend/src/hooks/useFormatTime.ts @@ -0,0 +1,29 @@ +import { useCallback } from "react"; +import dayjs from "dayjs"; +import duration from "dayjs/plugin/duration"; +import { useI18nContext } from "@/i18n/i18n-react"; + +dayjs.extend(duration); + +export const useFormatTime = () => { + const { LL } = useI18nContext(); + + const formatTime = useCallback((seconds: number) => { + if (seconds < 60) { + return LL.common.time.seconds({ count: seconds }); + } else if (seconds < 3600) { + const minutes = Math.floor(seconds / 60); + return LL.common.time.minutes({ count: minutes }); + } else if (seconds < 86400) { + const hours = Math.floor(seconds / 3600); + return LL.common.time.hours({ count: hours }); + } else { + const days = Math.floor(seconds / 86400); + return LL.common.time.days({ count: days }); + } + }, [LL.common.time]); + + return { + formatTime, + }; +}; \ No newline at end of file diff --git a/apps/frontend/src/hooks/useGovernanceSettings.ts b/apps/frontend/src/hooks/useGovernanceSettings.ts new file mode 100644 index 00000000..17cdcb81 --- /dev/null +++ b/apps/frontend/src/hooks/useGovernanceSettings.ts @@ -0,0 +1,94 @@ +import { getConfig } from "@repo/config"; +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { EnhancedClause } from "@vechain/vechain-kit"; +import { useVevoteSendTransaction } from "@/utils/hooks/useVevoteSendTransaction"; +import { useCallback } from "react"; + +type GovernanceSettingsProps = { + updateQuorumNumerator?: number; + setMinVotingDelay?: number; + setMinVotingDuration?: number; + setMaxVotingDuration?: number; + setMinStakedVetAmount?: bigint; +}; + +const contractAddress = getConfig(import.meta.env.VITE_APP_ENV).vevoteContractAddress; +const contractInterface = VeVote__factory.createInterface(); + +export const useGovernanceSettings = () => { + const buildClauses = useCallback((props: GovernanceSettingsProps) => { + const clauses: EnhancedClause[] = []; + + try { + const baseClause = { + to: contractAddress, + value: 0, + }; + + if (props.updateQuorumNumerator !== undefined) { + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("updateQuorumNumerator", [props.updateQuorumNumerator]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("updateQuorumNumerator"))), + comment: `Update quorum numerator to ${props.updateQuorumNumerator}`, + }; + clauses.push(clause as EnhancedClause); + } + + if (props.setMinVotingDelay !== undefined) { + const blocksValue = Math.floor(props.setMinVotingDelay / 10); + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("setMinVotingDelay", [blocksValue]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("setMinVotingDelay"))), + comment: `Set min voting delay to ${props.setMinVotingDelay} seconds (${blocksValue} blocks)`, + }; + clauses.push(clause as EnhancedClause); + } + + if (props.setMinVotingDuration !== undefined) { + const blocksValue = Math.floor(props.setMinVotingDuration / 10); + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("setMinVotingDuration", [blocksValue]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("setMinVotingDuration"))), + comment: `Set min voting duration to ${props.setMinVotingDuration} seconds (${blocksValue} blocks)`, + }; + clauses.push(clause as EnhancedClause); + } + + if (props.setMaxVotingDuration !== undefined) { + const blocksValue = Math.floor(props.setMaxVotingDuration / 10); + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("setMaxVotingDuration", [blocksValue]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("setMaxVotingDuration"))), + comment: `Set max voting duration to ${props.setMaxVotingDuration} seconds (${blocksValue} blocks)`, + }; + clauses.push(clause as EnhancedClause); + } + + if (props.setMinStakedVetAmount !== undefined) { + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("setMinStakedVetAmount", [props.setMinStakedVetAmount]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("setMinStakedVetAmount"))), + comment: `Set minimum staked VET amount to ${props.setMinStakedVetAmount.toString()}`, + }; + clauses.push(clause as EnhancedClause); + } + + return clauses; + } catch (error) { + console.error(error); + return []; + } + }, []); + + return useVevoteSendTransaction({ + clauseBuilder: buildClauses, + delayedRefetchKeys: [["veVoteInfo"]], + refetchDelay: 500, + maxRetries: 1, + }); +}; diff --git a/apps/frontend/src/hooks/useLevelMultipliers.ts b/apps/frontend/src/hooks/useLevelMultipliers.ts new file mode 100644 index 00000000..a7833cc6 --- /dev/null +++ b/apps/frontend/src/hooks/useLevelMultipliers.ts @@ -0,0 +1,45 @@ +import { getConfig } from "@repo/config"; +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { EnhancedClause } from "@vechain/vechain-kit"; +import { useVevoteSendTransaction } from "@/utils/hooks/useVevoteSendTransaction"; +import { useCallback } from "react"; + +type LevelMultipliersProps = { + updateLevelIdMultipliers: number[]; +}; + +const contractAddress = getConfig(import.meta.env.VITE_APP_ENV).vevoteContractAddress; +const contractInterface = VeVote__factory.createInterface(); + +export const useLevelMultipliers = () => { + const buildClauses = useCallback((props: LevelMultipliersProps) => { + const clauses: EnhancedClause[] = []; + + try { + const baseClause = { + to: contractAddress, + value: 0, + }; + + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("updateLevelIdMultipliers", [props.updateLevelIdMultipliers]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("updateLevelIdMultipliers"))), + comment: `Update level ID multipliers: [${props.updateLevelIdMultipliers.join(", ")}]`, + }; + clauses.push(clause as EnhancedClause); + + return clauses; + } catch (error) { + console.error(error); + return []; + } + }, []); + + return useVevoteSendTransaction({ + clauseBuilder: buildClauses, + delayedRefetchKeys: [["veVoteInfo"], ["levelMultipliers"]], + refetchDelay: 500, + maxRetries: 1, + }); +}; diff --git a/apps/frontend/src/hooks/useRoleManagement.ts b/apps/frontend/src/hooks/useRoleManagement.ts new file mode 100644 index 00000000..669eb90c --- /dev/null +++ b/apps/frontend/src/hooks/useRoleManagement.ts @@ -0,0 +1,55 @@ +import { getConfig } from "@repo/config"; +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { EnhancedClause } from "@vechain/vechain-kit"; +import { useVevoteSendTransaction } from "@/utils/hooks/useVevoteSendTransaction"; +import { useCallback } from "react"; +import { ROLES } from "@/pages/Admin/components/Users/UserManagementCard"; + +type RoleManagementProps = { + action: "grant" | "revoke"; + role: (typeof ROLES)[number]; + account: string; +}; + +const contractAddress = getConfig(import.meta.env.VITE_APP_ENV).vevoteContractAddress; +const contractInterface = VeVote__factory.createInterface(); + +export const useRoleManagement = () => { + const buildClauses = useCallback((props: RoleManagementProps) => { + const clauses: EnhancedClause[] = []; + + try { + const baseClause = { + to: contractAddress, + value: 0, + }; + + if (props.action === "grant") { + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("grantRole", [props.role, props.account]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("grantRole"))), + comment: `Grant role to ${props.account}`, + }; + clauses.push(clause as EnhancedClause); + } else if (props.action === "revoke") { + const clause = { + ...baseClause, + data: contractInterface.encodeFunctionData("revokeRole", [props.role, props.account]), + abi: JSON.parse(JSON.stringify(contractInterface.getFunction("revokeRole"))), + comment: `Revoke role from ${props.account}`, + }; + clauses.push(clause as EnhancedClause); + } + + return clauses; + } catch (error) { + console.error(error); + return []; + } + }, []); + + return useVevoteSendTransaction({ + clauseBuilder: buildClauses, + }); +}; diff --git a/apps/frontend/src/i18n/en/index.ts b/apps/frontend/src/i18n/en/index.ts index ec69906b..c9403b8a 100644 --- a/apps/frontend/src/i18n/en/index.ts +++ b/apps/frontend/src/i18n/en/index.ts @@ -68,6 +68,14 @@ const en = { stargate: "StarGate", learn_how_voting_power: "How voting power is obtained", discuss_on_discourse: "Join the discussion on Discourse", + common: { + time: { + seconds: "{count:number} {count|{1: second, *: seconds}}", + minutes: "{count:number} {count|{1: minute, *: minutes}}", + hours: "{count:number} {count|{1: hour, *: hours}}", + days: "{count:number} {count|{1: day, *: days}}", + }, + }, datepicker: { select_date: "Select date", previous_month: "Previous month", @@ -126,6 +134,7 @@ const en = { field_errors: { required: "Required", invalid_format: "Invalid format", + invalid_address: "Please enter a valid address", end_before_start: "The end date must be after the start date", end_before_today: "The end date must be in the future", start_after_today: "The start date must be in the future", @@ -422,6 +431,222 @@ const en = { governance_charter: "Governance Charter", }, }, + admin: { + title: "Admin Dashboard", + tabs: { + contracts: "Contracts", + utils: "Utils", + users: "Users", + governance_settings: "Governance Settings", + voting_power_timepoint: "Voting Power Query", + }, + contracts: { + vevote: "VeVote", + node_management: "Node Management", + stargate_nodes: "Stargate Nodes", + }, + vevote_contract: { + contract_address: "Contract Address:", + title: "VeVote Contract Information", + loading: "Loading VeVote Contract Information...", + error: "Error loading VeVote contract data: {error:string}", + no_data: "No VeVote contract data available", + contract_version: "Contract Version", + quorum_numerator: "Quorum Numerator", + quorum_denominator: "Quorum Denominator", + min_voting_delay: "Min Voting Delay", + min_voting_duration: "Min Voting Duration", + max_voting_duration: "Max Voting Duration", + min_staked_amount: "Min Staked Amount", + quorum_percentage: "{percentage:number}% required", + contract_info_title: "Contract Information", + }, + node_management: { + title: "Node Management Contract Information", + help_text: "Enter a wallet address to view detailed node ownership and delegation information for that account.", + user_address_label: "User Address", + user_address_placeholder: "Enter user address (0x...)", + load_button: "Load User Node Info", + loading_button: "Loading...", + loading_text: "Loading user node information...", + node_info_title: "Node Information for {address:string}", + is_node_holder: "Is Node Holder", + is_node_delegator: "Is Node Delegator", + owned_nodes: "Owned Nodes", + managed_nodes: "Managed Nodes", + ids_label: "IDs: {ids:string}", + yes: "Yes", + no: "No", + error: "Error loading node data: {error:string}", + card_title: "Node Information", + results_for: "Results for {address:string}", + no_results: "No node information available for this address", + methods_title: "Available Methods", + methods_description: + "This component demonstrates the NodeManagementService functionality. You can extend it to show additional statistics like total nodes, delegation stats, etc.", + }, + stargate_nodes: { + title: "Stargate NFT Contract Information", + loading: "Loading Stargate NFT Information...", + error: "Error loading Stargate NFT data: {error:string}", + no_data: "No Stargate NFT data available", + total_supply: "Total Supply", + available_levels: "Available Levels", + level_ids: "Level IDs: {ids:string}", + level_details_title: "Level Details", + table: { + level: "Level", + name: "Name", + is_x_node: "Is X-Node", + maturity_blocks: "Maturity Blocks", + vet_required: "VET Required", + circulating: "Circulating", + cap: "Cap", + }, + yes: "Yes", + no: "No", + not_available: "N/A", + contract_info_title: "Contract Information", + contract_description: + "This displays comprehensive information about the Stargate NFT contract including level configurations, supply information, and staking requirements.", + }, + format_seconds: "{number:number}s", + format_minutes_seconds: "{minutes:number} min ({seconds:number}s)", + format_days: "{number:number} days", + vet_format: "{amount:string} VET", + governance_settings: { + title: "Governance Settings", + description: "Configure VeVote governance parameters", + quorum_numerator_label: "Quorum Numerator", + quorum_numerator_help: "Required votes numerator (current: {current:number})", + min_voting_delay_label: "Min Voting Delay", + min_voting_delay_help: "Minimum delay before voting starts (in blocks)", + min_voting_duration_label: "Min Voting Duration", + min_voting_duration_help: "Minimum voting duration (in blocks)", + max_voting_duration_label: "Max Voting Duration", + max_voting_duration_help: "Maximum voting duration (in blocks)", + min_staked_vet_amount_label: "Min Staked VET Amount", + min_staked_vet_amount_help: "Minimum VET amount required to stake", + level_multipliers_label: "Level ID Multipliers", + level_multipliers_help: "Voting weight multipliers for each level ID (scaled by 100)", + validator_multiplier: "Validator", + strength: "Strength", + thunder: "Thunder", + mjolnir: "Mjolnir", + vethor_x: "VeThor X", + strength_x: "Strength X", + thunder_x: "Thunder X", + mjolnir_x: "Mjolnir X", + dawn: "Dawn Node", + lightning: "Lightning Node", + flash: "Flash Node", + level_id: "Level ID", + node_name: "Node Name", + current_multiplier: "Current", + new_multiplier: "New Value", + update_settings: "Update Settings", + update_governance_settings: "Update Governance Settings", + update_multipliers: "Update Multipliers", + updating: "Updating...", + success_title: "Settings Updated Successfully", + success_description: "Governance settings have been updated and are now active.", + error_title: "Failed to Update Settings", + error_description: "There was an error updating the governance settings: {error:string}", + invalid_range: "Value must be between {min:number} and {max:number}", + required_field: "This field is required", + current_value: "Current: {value:string}", + }, + user_management: { + title: "User Management", + description: "Grant or revoke roles for VeVote governance", + user_address_label: "User Address", + user_address_placeholder: "Enter user address (0x...)", + role_label: "Role", + role_placeholder: "Select a role", + current_roles_label: "Current Roles:", + checking_roles: "Checking roles...", + no_roles_assigned: "No roles assigned", + }, + role_users: { + title: "Role Users", + help_text: "Select a role to view all users who have been granted that specific role.", + role_label: "Role", + role_placeholder: "Select a role to query", + query_button: "Query Role Users", + loading: "Loading...", + loading_text: "Fetching users with selected role...", + results_title: "Users with Role", + user_count: "{count:number} {count|{1: user, *: users}}", + role_selected: "Role: {role:string}", + granted_at: "Granted: {date:string}", + view_tx: "View TX", + no_users: "No users found with this role", + scrollable_hint: "Scroll to see more users", + error_description: "Error fetching role users: {error:string}", + }, + user_role_checker: { + title: "Your Permissions", + connect_wallet_message: "Connect wallet to see your roles", + checking_roles: "Checking your roles...", + }, + voting_power_timepoint: { + address: "Address:", + timepoint: "Timepoint:", + master_address: "Master Address:", + title: "Voting Power at Timepoint", + description: "Query historical voting power for any wallet at a specific timepoint", + address_label: "Wallet Address", + address_placeholder: "Enter wallet address (0x...)", + timepoint_label: "Timepoint", + timepoint_placeholder: "Enter timepoint", + master_address_label: "Master Address", + master_address_placeholder: "Enter master address for validator power (0x...)", + query_button: "Query Voting Power", + querying: "Querying...", + results_title: "Voting Power Results", + node_power_label: "Node-based Power:", + validator_power_label: "Validator Power:", + total_power_label: "Total Power:", + no_results: "No results to display", + invalid_address: "Please enter a valid address", + invalid_timepoint: "Please enter a valid timepoint/block number", + address_required: "Wallet address is required", + timepoint_required: "Timepoint is required", + error_title: "Query Failed", + error_description: "There was an error querying voting power: {error:string}", + help_text: + "Enter a wallet address and timepoint to view historical voting power. Optionally provide a master address to check validator-delegated power.", + }, + unknown_error: "Unknown error", + sensitive_operation_warning: "This operation is sensitive. Please use with caution.", + your_permissions: "Your Permissions", + common_roles: { + DEFAULT_ADMIN_ROLE: "Default Admin", + EXECUTOR_ROLE: "Executor", + SETTINGS_MANAGER_ROLE: "Settings Manager", + NODE_WEIGHT_MANAGER_ROLE: "Node Weight Manager", + UPGRADER_ROLE: "Upgrader", + WHITELISTED_ROLE: "Whitelisted", + WHITELIST_ADMIN_ROLE: "Whitelist Admin", + PAUSER_ROLE: "Pauser", + LEVEL_OPERATOR_ROLE: "Level Operator", + MANAGER_ROLE: "Manager", + WHITELISTER_ROLE: "Whitelister", + grant_role: "Grant Role", + revoke_role: "Revoke Role", + granting: "Granting...", + revoking: "Revoking...", + grant_success_title: "Role Granted Successfully", + grant_success_description: "The role has been granted to the user.", + revoke_success_title: "Role Revoked Successfully", + revoke_success_description: "The role has been revoked from the user.", + error_title: "Role Operation Failed", + error_description: "There was an error with the role operation: {error:string}", + invalid_address: "Please enter a valid address", + role_required: "Please select a role", + address_required: "User address is required", + }, + }, } satisfies BaseTranslation; export default en; diff --git a/apps/frontend/src/i18n/i18n-react.tsx b/apps/frontend/src/i18n/i18n-react.tsx index 75b2fd62..6c5bce09 100644 --- a/apps/frontend/src/i18n/i18n-react.tsx +++ b/apps/frontend/src/i18n/i18n-react.tsx @@ -1,21 +1,16 @@ // This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. /* eslint-disable */ -import { useContext } from "react"; -import { initI18nReact } from "typesafe-i18n/react"; -import type { I18nContextType } from "typesafe-i18n/react"; -import type { Formatters, Locales, TranslationFunctions, Translations } from "./i18n-types.js"; -import { loadedFormatters, loadedLocales } from "./i18n-util.js"; +import { useContext } from 'react' +import { initI18nReact } from 'typesafe-i18n/react' +import type { I18nContextType } from 'typesafe-i18n/react' +import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types.js' +import { loadedFormatters, loadedLocales } from './i18n-util.js' -const { component: TypesafeI18n, context: I18nContext } = initI18nReact< - Locales, - Translations, - TranslationFunctions, - Formatters ->(loadedLocales, loadedFormatters); +const { component: TypesafeI18n, context: I18nContext } = initI18nReact(loadedLocales, loadedFormatters) -const useI18nContext = (): I18nContextType => useContext(I18nContext); +const useI18nContext = (): I18nContextType => useContext(I18nContext) -export { I18nContext, useI18nContext }; +export { I18nContext, useI18nContext } -export default TypesafeI18n; +export default TypesafeI18n diff --git a/apps/frontend/src/i18n/i18n-types.ts b/apps/frontend/src/i18n/i18n-types.ts index 4beffb80..909040c0 100644 --- a/apps/frontend/src/i18n/i18n-types.ts +++ b/apps/frontend/src/i18n/i18n-types.ts @@ -1,2737 +1,4362 @@ // This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. /* eslint-disable */ -import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from "typesafe-i18n"; +import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n' -export type BaseTranslation = BaseTranslationType; -export type BaseLocale = "en"; +export type BaseTranslation = BaseTranslationType +export type BaseLocale = 'en' -export type Locales = "en"; +export type Locales = + | 'en' -export type Translation = RootTranslation; +export type Translation = RootTranslation -export type Translations = RootTranslation; +export type Translations = RootTranslation type RootTranslation = { - /** - * L​e​f​t - */ - left: string; - /** - * b​y - */ - by: string; - /** - * N​o​t​ ​p​u​b​l​i​s​h​e​d - */ - not_published: string; - /** - * H​o​m​e​p​a​g​e - */ - homepage: string; - /** - * B​a​c​k - */ - back: string; - /** - * S​t​a​r​t - */ - start: string; - /** - * E​n​d - */ - end: string; - /** - * E​d​i​t - */ - edit: string; - /** - * O​n - */ - on: string; - /** - * A​l​l - */ - all: string; - /** - * F​i​n​i​s​h​e​d - */ - finished: string; - /** - * E​x​e​c​u​t​e​d - */ - executed: string; - /** - * S​h​o​w​ ​m​o​r​e - */ - show_more: string; - /** - * E​x​i​t - */ - exit: string; - /** - * N​e​x​t - */ - next: string; - /** - * L​e​a​r​n​ ​m​o​r​e - */ - learn_more: string; - /** - * S​e​e​ ​d​e​t​a​i​l​s - */ - see_details: string; - /** - * S​e​l​e​c​t - */ - select: string; - /** - * S​e​l​e​c​t​ ​b​e​t​w​e​e​n - */ - select_between: string; - /** - * a​n​d - */ - and: string; - /** - * o​n​e - */ - one: string; - /** - * V​o​t​i​n​g - */ - voting: string; - /** - * V​o​t​e​r​s - */ - voters: string; - /** - * M​o​s​t​ ​v​o​t​e​d - */ - most_voted: string; - /** - * V​o​t​e​s - */ - votes: string; - /** - * D​e​l​e​t​e - */ - delete: string; - /** - * C​a​n​c​e​l - */ - cancel: string; - /** - * % - */ - percentage: string; - /** - * S​u​b​m​i​t - */ - submit: string; - /** - * S​u​b​m​i​t​ ​V​o​t​e - */ - submit_vote: string; - /** - * V​o​t​i​n​g​ ​p​o​w​e​r - */ - voting_power: string; - /** - * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r - */ - your_voting_power: string; - /** - * V​o​t​e​d - */ - voted: string; - /** - * V​o​t​e - */ - vote: string; - /** - * W​a​l​l​e​t - */ - wallet: string; - /** - * B​l​o​c​k​ ​# - */ - block: string; - /** - * N​o​d​e - */ - node: string; - /** - * G​o​ ​b​a​c​k - */ - go_back: string; - /** - * O​p​t​i​o​n​a​l - */ - optional: string; - /** - * B​u​y​ ​a​ ​N​o​d​e - */ - buy_a_node: string; - /** - * {​c​u​r​r​e​n​t​}​/​{​m​a​x​} - * @param {number} current - * @param {number} max - */ - filed_length: RequiredParams<"current" | "max">; - /** - * U​p​l​o​a​d - */ - upload: string; - /** - * C​o​p​i​e​d​ ​t​o​ ​c​l​i​p​b​o​a​r​d - */ - copied_to_clipboard: string; - /** + /** + * L​e​f​t + */ + left: string + /** + * b​y + */ + by: string + /** + * N​o​t​ ​p​u​b​l​i​s​h​e​d + */ + not_published: string + /** + * H​o​m​e​p​a​g​e + */ + homepage: string + /** + * B​a​c​k + */ + back: string + /** + * S​t​a​r​t + */ + start: string + /** + * E​n​d + */ + end: string + /** + * E​d​i​t + */ + edit: string + /** + * O​n + */ + on: string + /** + * A​l​l + */ + all: string + /** + * F​i​n​i​s​h​e​d + */ + finished: string + /** + * E​x​e​c​u​t​e​d + */ + executed: string + /** + * S​h​o​w​ ​m​o​r​e + */ + show_more: string + /** + * E​x​i​t + */ + exit: string + /** + * N​e​x​t + */ + next: string + /** + * L​e​a​r​n​ ​m​o​r​e + */ + learn_more: string + /** + * S​e​e​ ​d​e​t​a​i​l​s + */ + see_details: string + /** + * S​e​l​e​c​t + */ + select: string + /** + * S​e​l​e​c​t​ ​b​e​t​w​e​e​n + */ + select_between: string + /** + * a​n​d + */ + and: string + /** + * o​n​e + */ + one: string + /** + * V​o​t​i​n​g + */ + voting: string + /** + * V​o​t​e​r​s + */ + voters: string + /** + * M​o​s​t​ ​v​o​t​e​d + */ + most_voted: string + /** + * V​o​t​e​s + */ + votes: string + /** + * D​e​l​e​t​e + */ + 'delete': string + /** + * C​a​n​c​e​l + */ + cancel: string + /** + * % + */ + percentage: string + /** + * S​u​b​m​i​t + */ + submit: string + /** + * S​u​b​m​i​t​ ​V​o​t​e + */ + submit_vote: string + /** + * V​o​t​i​n​g​ ​p​o​w​e​r + */ + voting_power: string + /** + * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r + */ + your_voting_power: string + /** + * V​o​t​e​d + */ + voted: string + /** + * V​o​t​e + */ + vote: string + /** + * W​a​l​l​e​t + */ + wallet: string + /** + * B​l​o​c​k​ ​# + */ + block: string + /** + * N​o​d​e + */ + node: string + /** + * G​o​ ​b​a​c​k + */ + go_back: string + /** + * O​p​t​i​o​n​a​l + */ + optional: string + /** + * B​u​y​ ​a​ ​N​o​d​e + */ + buy_a_node: string + /** + * {​c​u​r​r​e​n​t​}​/​{​m​a​x​} + * @param {number} current + * @param {number} max + */ + filed_length: RequiredParams<'current' | 'max'> + /** + * U​p​l​o​a​d + */ + upload: string + /** + * C​o​p​i​e​d​ ​t​o​ ​c​l​i​p​b​o​a​r​d + */ + copied_to_clipboard: string + /** * I​m​a​g​e​ ​s​i​z​e​ ​s​h​o​u​l​d​ ​b​e​ ​1​2​8​0​x​5​1​2​p​x​ ​(​r​a​t​i​o​ ​3​:​1​)​.​ ​J​P​G​,​ ​P​N​G​ ​o​r​ ​S​V​G​ ​o​f​ ​m​a​x​i​m​u​m​ ​o​f​ ​{​s​i​z​e​}​M​B​. * @param {number} size */ - file_upload_description: RequiredParams<"size">; - /** - * S​e​l​e​c​t​ ​d​a​t​e - */ - select_date: string; - /** - * S​e​l​e​c​t​ ​t​i​m​e - */ - select_time: string; - /** - * M​a​x​i​m​u​m - */ - maximum: string; - /** - * M​i​n​i​m​u​m - */ - minimum: string; - /** - * O​p​t​i​o​n​ ​{​i​n​d​e​x​} - * @param {number} index - */ - number_option: RequiredParams<"index">; - /** - * C​o​n​t​i​n​u​e - */ - continue: string; - /** - * R​e​s​u​l​t​s - */ - results: string; - /** - * D​e​s​c​r​i​p​t​i​o​n - */ - description: string; - /** - * P​r​e​v​i​e​w - */ - preview: string; - /** - * C​l​o​s​e - */ - close: string; - /** - * C​o​n​f​i​r​m - */ - confirm: string; - /** - * T​r​y​ ​A​g​a​i​n - */ - try_again: string; - /** - * R​e​a​d​ ​f​u​l​l​ ​d​e​s​c​r​i​p​t​i​o​n - */ - read_full_description: string; - /** - * D​i​s​c​o​n​n​e​c​t - */ - disconnect: string; - /** - * C​o​n​n​e​c​t​ ​W​a​l​l​e​t - */ - connect_wallet: string; - /** - * C​o​n​n​e​c​t​ ​y​o​u​r​ ​w​a​l​l​e​t​ ​t​o​ ​v​o​t​e - */ - connect_wallet_to_vote: string; - /** - * C​o​m​m​e​n​t - */ - comment: string; - /** - * A​d​d​ ​a​ ​c​o​m​m​e​n​t​ ​t​o​ ​y​o​u​r​ ​v​o​t​e​.​.​. - */ - comment_placeholder: string; - /** - * M​i​g​r​a​t​e - */ - migrate: string; - /** - * S​t​a​r​G​a​t​e - */ - stargate: string; - /** - * H​o​w​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​i​s​ ​o​b​t​a​i​n​e​d - */ - learn_how_voting_power: string; - /** - * J​o​i​n​ ​t​h​e​ ​d​i​s​c​u​s​s​i​o​n​ ​o​n​ ​D​i​s​c​o​u​r​s​e - */ - discuss_on_discourse: string; - datepicker: { - /** - * S​e​l​e​c​t​ ​d​a​t​e - */ - select_date: string; - /** - * P​r​e​v​i​o​u​s​ ​m​o​n​t​h - */ - previous_month: string; - /** - * N​e​x​t​ ​m​o​n​t​h - */ - next_month: string; - /** - * T​o​d​a​y - */ - today: string; - weekdays: { - /** - * M​o​n - */ - mon: string; - /** - * T​u​e - */ - tue: string; - /** - * W​e​d - */ - wed: string; - /** - * T​h​u - */ - thu: string; - /** - * F​r​i - */ - fri: string; - /** - * S​a​t - */ - sat: string; - /** - * S​u​n - */ - sun: string; - }; - months: { - /** - * J​a​n​u​a​r​y - */ - january: string; - /** - * F​e​b​r​u​a​r​y - */ - february: string; - /** - * M​a​r​c​h - */ - march: string; - /** - * A​p​r​i​l - */ - april: string; - /** - * M​a​y - */ - may: string; - /** - * J​u​n​e - */ - june: string; - /** - * J​u​l​y - */ - july: string; - /** - * A​u​g​u​s​t - */ - august: string; - /** - * S​e​p​t​e​m​b​e​r - */ - september: string; - /** - * O​c​t​o​b​e​r - */ - october: string; - /** - * N​o​v​e​m​b​e​r - */ - november: string; - /** - * D​e​c​e​m​b​e​r - */ - december: string; - }; - }; - timepicker: { - /** - * S​e​l​e​c​t​ ​t​i​m​e - */ - select_time: string; - /** - * S​e​l​e​c​t​ ​t​i​m​e​ ​(​U​T​C​) - */ - select_time_24h: string; - /** - * H​o​u​r​s - */ - hours: string; - /** - * M​i​n​u​t​e​s - */ - minutes: string; - /** - * A​l​l​ ​t​i​m​e​s​ ​a​r​e​ ​i​n​ ​U​T​C - */ - utc_notice: string; - }; - home: { - /** - * H​o​m​e - */ - title: string; - /** - * G​o​ ​t​o​ ​p​r​o​p​o​s​a​l​s - */ - go_to_proposals: string; - }; - node_names: { - /** - * n​o​t​ ​d​e​f​i​n​e​d - */ - none: string; - /** - * S​t​r​e​n​g​t​h - */ - strength: string; - /** - * T​h​u​n​d​e​r - */ - thunder: string; - /** - * M​j​o​l​n​i​r - */ - mjolnir: string; - /** - * V​e​T​h​o​r​ ​X - */ - vethorx: string; - /** - * S​t​r​e​n​g​t​h​ ​X - */ - strengthx: string; - /** - * T​h​u​n​d​e​r​ ​X - */ - thunderx: string; - /** - * M​j​o​l​n​i​r​ ​X - */ - mjolnirx: string; - /** - * F​l​a​s​h - */ - flash: string; - /** - * L​i​g​h​t​n​i​n​g - */ - lightning: string; - /** - * D​a​w​n - */ - dawn: string; - /** - * V​a​l​i​d​a​t​o​r - */ - validator: string; - /** - * V​a​l​i​d​a​t​o​r​ ​(​i​n​a​c​t​i​v​e​) - */ - inactive_validator: string; - }; - field_errors: { - /** - * R​e​q​u​i​r​e​d - */ - required: string; - /** - * I​n​v​a​l​i​d​ ​f​o​r​m​a​t - */ - invalid_format: string; - /** - * T​h​e​ ​e​n​d​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​a​f​t​e​r​ ​t​h​e​ ​s​t​a​r​t​ ​d​a​t​e - */ - end_before_start: string; - /** - * T​h​e​ ​e​n​d​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​i​n​ ​t​h​e​ ​f​u​t​u​r​e - */ - end_before_today: string; - /** - * T​h​e​ ​s​t​a​r​t​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​i​n​ ​t​h​e​ ​f​u​t​u​r​e - */ - start_after_today: string; - /** - * T​h​e​ ​e​n​d​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​w​i​t​h​i​n​ ​{​d​a​y​s​}​ ​d​a​y​s​ ​o​f​ ​t​h​e​ ​s​t​a​r​t​ ​d​a​t​e - * @param {string} days - */ - end_after_max_duration: RequiredParams<"days">; - /** - * N​o​ ​v​o​t​e​r​s​ ​f​o​u​n​d​ ​m​a​t​c​h​i​n​g​ ​y​o​u​r​ ​s​e​a​r​c​h​ ​c​r​i​t​e​r​i​a​. - */ - failed_load_voters: string; - descriptions_errors: { - /** - * P​l​e​a​s​e​ ​r​e​p​l​a​c​e​ ​p​l​a​c​e​h​o​l​d​e​r​ ​t​e​x​t​ ​w​i​t​h​ ​y​o​u​r​ ​o​w​n​ ​c​o​n​t​e​n​t​ ​b​e​f​o​r​e​ ​s​u​b​m​i​t​t​i​n​g​ ​t​h​e​ ​p​r​o​p​o​s​a​l​. - */ - placeholders_not_replaced: string; - /** - * D​e​s​c​r​i​p​t​i​o​n​ ​c​a​n​n​o​t​ ​b​e​ ​e​m​p​t​y​.​ ​P​l​e​a​s​e​ ​p​r​o​v​i​d​e​ ​c​o​n​t​e​n​t​ ​f​o​r​ ​y​o​u​r​ ​p​r​o​p​o​s​a​l​. - */ - empty_description: string; - }; - /** - * T​h​e​ ​D​i​s​c​o​u​r​s​e​ ​t​o​p​i​c​ ​d​o​e​s​ ​n​o​t​ ​e​x​i​s​t​ ​o​r​ ​i​s​ ​n​o​t​ ​a​c​c​e​s​s​i​b​l​e - */ - discourse_topic_not_exist: string; - }; - voting_list: { - /** - * V​o​t​i​n​g​ ​o​p​t​i​o​n​s​: - */ - voting_options: string; - /** - * S​e​l​e​c​t​ ​a​n​ ​o​p​t​i​o​n​ ​t​o​ ​v​o​t​e​: - */ - option_to_vote: string; - /** - * V​o​t​i​n​g​ ​h​a​s​ ​n​o​t​ ​s​t​a​r​t​e​d​ ​y​e​t - */ - voting_has_not_started_yet: string; - /** - * P​l​e​a​s​e​ ​c​o​n​n​e​c​t​ ​y​o​u​r​ ​w​a​l​l​e​t - */ - please_connect_your_wallet: string; - /** - * Y​o​u​ ​h​a​v​e​ ​a​l​r​e​a​d​y​ ​v​o​t​e​d - */ - you_have_already_voted: string; - /** - * Y​o​u​ ​d​o​n​'​t​ ​h​a​v​e​ ​e​n​o​u​g​h​ ​v​o​t​i​n​g​ ​p​o​w​e​r - */ - you_dont_have_enough_voting_power: string; - }; - proposal: { - /** - * P​r​o​p​o​s​a​l - */ - title: string; - /** - * P​r​o​p​o​s​e​d​ ​b​y - */ - proposed_by: string; - /** - * V​o​t​i​n​g​ ​c​a​l​e​n​d​a​r - */ - voting_calendar: string; - /** - * C​o​n​f​i​r​m​ ​i​n​ ​y​o​u​r​ ​w​a​l​l​e​t​.​.​. - */ - confirm_in_your_wallet: string; - /** - * W​h​o​ ​c​a​n​ ​v​o​t​e - */ - who_can_vote: string; - /** - * V​e​C​h​a​i​n​ ​F​o​u​n​d​a​t​i​o​n - */ - vechain_foundation: string; - /** - * N​o​d​e​ ​h​o​l​d​e​r​s​ ​w​i​t​h​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​w​i​l​l​ ​b​e​ ​a​b​l​e​ ​t​o​ ​v​o​t​e​ ​o​n​ ​t​h​i​s​ ​p​r​o​p​o​s​a​l​. - */ - node_holders: string; - /** - * V​o​t​i​n​g​ ​w​i​l​l​ ​s​t​a​r​t​ ​{​d​a​t​e​} - * @param {string} date - */ - voting_will_start: RequiredParams<"date">; - /** - * S​e​e​ ​y​o​u​r​ ​v​o​t​e​ ​d​e​t​a​i​l​s - */ - see_your_vote: string; - /** - * S​e​e​ ​a​l​l​ ​(​{​v​o​t​e​r​s​}​)​ ​v​o​t​e​r​s - * @param {number} voters - */ - see_all_voters: RequiredParams<"voters">; - /** - * S​e​e​ ​f​i​r​s​t​ ​v​o​t​e​r - */ - see_first_voter: string; - /** - * M​a​r​k​ ​a​s​ ​e​x​e​c​u​t​e​d - */ - mark_as_executed: string; - /** - * B​u​y​ ​a​n​o​t​h​e​r​ ​n​o​d​e​ ​t​o​ ​i​n​c​r​e​a​s​e​ ​y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​o​n​ ​f​u​t​u​r​e​ ​p​r​o​p​o​s​a​l​s​. - */ - buy_another_node: string; - /** - * V​o​t​i​n​g​ ​i​s​ ​o​n​l​y​ ​p​o​s​s​i​b​l​e​ ​f​o​r​ ​N​o​d​e​ ​h​o​l​d​e​r​s​.​ ​B​u​y​ ​a​ ​n​o​d​e​ ​t​o​ ​v​o​t​e​ ​o​n​ ​f​u​t​u​r​e​ ​p​r​o​p​o​s​a​l​s​ ​o​r​ ​i​n​c​r​e​a​s​e​ ​y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​. - */ - buy_a_node: string; - vote_success: { - /** - * V​o​t​e​ ​s​u​b​m​i​t​t​e​d​! - */ - title: string; - /** - * Y​o​u​r​ ​v​o​t​e​ ​w​a​s​ ​s​u​b​m​i​t​t​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​. - */ - description: string; - }; - cancel_proposal: { - /** - * C​a​n​c​e​l​ ​p​r​o​p​o​s​a​l - */ - title: string; - /** - * C​a​n​c​e​l​i​n​g​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​m​e​a​n​s​ ​i​t​ ​t​h​e​ ​v​o​t​i​n​g​ ​w​i​l​l​ ​n​o​t​ ​t​a​k​e​ ​p​l​a​c​e​ ​a​n​d​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​w​i​l​l​ ​n​o​t​ ​h​a​v​e​ ​n​o​ ​r​e​s​u​l​t​s​. - */ - description: string; - /** - * R​e​a​s​o​n - */ - reason: string; - /** - * W​r​i​t​e​ ​t​h​e​ ​r​e​a​s​o​n​ ​f​o​r​ ​c​a​n​c​e​l​l​a​t​i​o​n​.​.​. - */ - reason_placeholder: string; - /** - * P​r​o​p​o​s​a​l​ ​c​a​n​c​e​l​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y - */ - success_title: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​b​e​e​n​ ​c​a​n​c​e​l​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​.​ ​V​o​t​i​n​g​ ​w​i​l​l​ ​n​o​t​ ​t​a​k​e​ ​p​l​a​c​e​. - */ - success_description: string; - }; - execute_proposal: { - /** - * M​a​r​k​ ​a​s​ ​E​x​e​c​u​t​e​d - */ - title: string; - /** - * I​f​ ​t​h​e​ ​a​c​t​i​o​n​s​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​v​e​ ​a​l​r​e​a​d​y​ ​b​e​e​n​ ​e​x​e​c​u​t​e​d​,​ ​y​o​u​ ​c​a​n​ ​m​a​r​k​ ​t​h​i​s​ ​a​p​p​r​o​v​e​d​ ​p​r​o​p​o​s​a​l​ ​a​s​ ​e​x​e​c​u​t​e​d​ ​f​o​r​ ​t​h​e​ ​v​o​t​e​r​s​ ​t​o​ ​k​n​o​w​. - */ - description: string; - /** - * E​x​e​c​u​t​i​o​n​ ​/​ ​T​r​a​n​s​a​c​t​i​o​n​ ​d​e​t​a​i​l​s - */ - label: string; - /** - * I​n​s​e​r​t​ ​l​i​n​k​ ​w​i​t​h​ ​t​h​e​ ​p​r​o​o​f​ ​o​f​ ​t​h​e​ ​e​x​e​c​u​t​i​o​n - */ - link_placeholder: string; - }; - delete_proposal: { - /** - * D​e​l​e​t​e​ ​p​r​o​p​o​s​a​l - */ - title: string; - /** - * I​f​ ​y​o​u​ ​d​e​l​e​t​e​ ​t​h​i​s​ ​d​r​a​f​t​,​ ​a​l​l​ ​t​h​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​p​o​s​s​i​b​l​e​ ​t​o​ ​r​e​c​o​v​e​r​ ​a​n​y​m​o​r​e​. - */ - description: string; - /** - * A​r​e​ ​y​o​u​ ​s​u​r​e​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​i​t​? - */ - confirmation: string; - /** - * N​o​,​ ​g​o​ ​b​a​c​k - */ - no_go_back: string; - /** - * Y​e​s​,​ ​D​e​l​e​t​e - */ - yes_delete: string; - }; - info_box: { - info: { - /** - * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​t​i​o​n - */ - title: string; - /** - * A​ ​m​i​n​i​m​u​m​ ​o​f​ ​{​q​u​o​r​u​m​}​%​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​m​u​s​t​ ​b​e​ ​r​e​a​c​h​e​d​ ​t​o​ ​v​a​l​i​d​a​t​e​ ​t​h​e​ ​v​o​t​i​n​g​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​a​n​d​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. - * @param {number} quorum - */ - description: RequiredParams<"quorum">; - }; - approved: { - /** - * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​r​e​a​c​h​e​d - */ - title: string; - /** - * T​h​e​ ​v​o​t​i​n​g​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​r​e​a​c​h​e​d​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​r​e​q​u​i​r​e​d​ ​o​f​ ​{​q​u​o​r​u​m​}​%​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. - * @param {number} quorum - */ - description: RequiredParams<"quorum">; - }; - executed: { - /** - * P​r​o​p​o​s​a​l​ ​A​p​p​r​o​v​e​d​ ​a​n​d​ ​E​x​e​c​u​t​e​d - */ - title: string; - /** - * T​h​e​ ​v​o​t​i​n​g​ ​a​p​p​r​o​v​e​d​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​a​n​d​ ​t​h​e​ ​a​c​t​i​o​n​s​ ​h​a​v​e​ ​b​e​e​n​ ​e​x​e​c​u​t​e​d​. - */ - description: string; - }; - "min-not-reached": { - /** - * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​n​o​t​ ​r​e​a​c​h​e​d - */ - title: string; - /** - * T​h​e​ ​v​o​t​i​n​g​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​d​i​d​n​’​t​ ​r​e​a​c​h​e​d​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​r​e​q​u​i​r​e​d​ ​o​f​ ​{​q​u​o​r​u​m​}​%​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. - * @param {number} quorum - */ - description: RequiredParams<"quorum">; - }; - rejected: { - /** - * P​r​o​p​o​s​a​l​ ​R​e​j​e​c​t​e​d - */ - title: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​d​i​d​n​’​t​ ​g​e​t​ ​e​n​o​u​g​h​ ​v​o​t​e​s​ ​i​n​ ​f​a​v​o​r​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. - */ - description: string; - }; - canceled: { - /** - * P​r​o​p​o​s​a​l​ ​C​a​n​c​e​l​e​d - */ - title: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​w​a​s​ ​c​a​n​c​e​l​e​d​ ​b​y​ ​V​e​C​h​a​i​n​ ​o​r​ ​t​h​e​ ​p​r​o​p​o​s​e​r​ ​b​y​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​r​e​a​s​o​n​: - */ - description: string; - }; - }; - voting_power: { - /** - * G​e​t​ ​m​o​r​e​ ​v​o​t​i​n​g​ ​p​o​w​e​r - */ - get_more_voting_power: string; - /** - * G​e​t​ ​v​o​t​i​n​g​ ​p​o​w​e​r - */ - get_voting_power: string; - /** - * V​o​t​i​n​g​ ​p​o​w​e​r - */ - title: string; - /** - * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​w​a​s​ ​c​a​l​c​u​l​a​t​e​d​ ​a​t​ ​t​h​e​ ​t​i​m​e​ ​o​f​ ​t​h​e​ ​s​n​a​p​s​h​o​t​ ​{​s​n​a​p​s​h​o​t​}​. - * @param {string} snapshot - */ - calculation: RequiredParams<"snapshot">; - /** - * T​o​t​a​l​ ​v​o​t​i​n​g​ ​p​o​w​e​r - */ - total_voting_power: string; - warnings: { - /** - * T​h​e​ ​c​o​n​n​e​c​t​e​d​ ​w​a​l​l​e​t​ ​h​a​s​ ​n​o​ ​v​o​t​i​n​g​ ​p​o​w​e​r - */ - zero_voting_power: string; - /** - * Y​o​u​ ​h​a​v​e​ ​l​e​g​a​c​y​ ​n​o​d​e​s​ ​t​h​a​t​ ​h​a​v​e​n​’​t​ ​b​e​e​n​ ​m​i​g​r​a​t​e​d​ ​y​e​t​.​ ​ ​M​i​g​r​a​t​e​ ​t​o​ ​g​e​t​ ​m​o​r​e​ ​v​o​t​i​n​g​ ​p​o​w​e​r​. - */ - legacy_node: string; - delegated: { - /** - * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​i​s​ ​d​e​l​e​g​a​t​e​d - */ - title: string; - /** - * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​i​s​ ​d​e​l​e​g​a​t​e​d​ ​t​o​ ​a​n​o​t​h​e​r​ ​n​o​d​e - */ - description: string; - }; - }; - }; - voters_table: { - filters: { - /** - * S​e​a​r​c​h​ ​b​y​ ​a​d​d​r​e​s​s​.​.​. - */ - search_by_address: string; - /** - * V​o​t​i​n​g​ ​o​p​t​i​o​n​s - */ - voting_options: string; - /** - * N​o​d​e - */ - node: string; - /** - * S​o​r​t​ ​b​y - */ - sort_by: string; - }; - header: { - /** - * D​a​t​e - */ - date: string; - /** - * A​d​d​r​e​s​s - */ - address: string; - /** - * N​o​d​e - */ - node: string; - /** - * N​o​d​e​ ​I​D - */ - node_id: string; - /** - * P​o​w​e​r - */ - voting_power: string; - /** - * O​p​t​i​o​n - */ - voted_option: string; - /** - * T​r​a​n​s​a​c​t​i​o​n​ ​I​D - */ - transaction_id: string; - }; - }; - create: { - /** - * P​r​e​v​i​e​w​i​n​g​ ​p​r​o​p​o​s​a​l - */ - previewing: string; - /** - * C​r​e​a​t​e​ ​P​r​o​p​o​s​a​l - */ - title: string; - /** - * {​c​u​r​r​e​n​t​}​ ​o​f​ ​{​t​o​t​a​l​} - * @param {number} current - * @param {number} total - */ - steps: RequiredParams<"current" | "total">; - /** - * A​d​d​ ​t​h​e​ ​m​a​i​n​ ​d​e​t​a​i​l​s​ ​a​n​d​ ​s​e​t​u​p​ ​t​h​e​ ​c​a​l​e​n​d​a​r - */ - voting_details_desc: string; - /** - * D​e​f​i​n​e​ ​t​h​e​ ​v​o​t​i​n​g​ ​s​e​t​u​p​ ​d​e​t​a​i​l​s - */ - voting_setup_desc: string; - /** - * R​e​v​i​e​w​ ​a​l​l​ ​t​h​e​ ​d​e​t​a​i​l​s​ ​b​e​f​o​r​e​ ​p​u​b​l​i​s​h​i​n​g - */ - voting_summary_desc: string; - /** - * A​d​d​ ​n​e​w​ ​o​p​t​i​o​n - */ - add_new_option: string; - exit_proposal: { - /** - * E​x​i​t​ ​p​r​o​p​o​s​a​l​ ​c​r​e​a​t​i​o​n - */ - title: string; - /** - * B​y​ ​e​x​i​t​i​n​g​ ​y​o​u​ ​l​o​s​e​ ​a​l​l​ ​t​h​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​r​i​t​t​e​n​ ​o​r​ ​y​o​u​ ​c​a​n​ ​s​a​v​e​ ​t​h​i​s​ ​p​r​o​p​o​s​a​l​ ​a​s​ ​a​ ​d​r​a​f​t​ ​a​n​d​ ​f​i​n​i​s​h​ ​l​a​t​e​r​? - */ - description: string; - /** - * Y​o​u​ ​w​i​l​l​ ​l​o​s​e​ ​a​l​l​ ​e​n​t​e​r​e​d​ ​i​n​f​o​r​m​a​t​i​o​n​ ​i​f​ ​y​o​u​ ​e​x​i​t​ ​n​o​w​.​ ​S​a​v​i​n​g​ ​a​s​ ​a​ ​d​r​a​f​t​ ​i​s​ ​o​n​l​y​ ​a​v​a​i​l​a​b​l​e​ ​o​n​ ​t​h​e​ ​f​i​n​a​l​ ​s​t​e​p​ ​o​f​ ​t​h​e​ ​f​o​r​m​. - */ - description_last_step: string; - /** - * B​e​ ​a​w​a​r​e​ ​t​h​a​t​ ​a​ ​d​r​a​f​t​ ​p​r​o​p​o​s​a​l​ ​a​l​r​e​a​d​y​ ​e​x​i​s​t​s​.​ ​S​a​v​i​n​g​ ​n​o​w​ ​w​i​l​l​ ​o​v​e​r​w​r​i​t​e​ ​y​o​u​r​ ​p​r​e​v​i​o​u​s​ ​d​r​a​f​t​. - */ - description_draft_exist: string; - /** - * E​x​i​t​ ​p​r​o​p​o​s​a​l - */ - exit_button: string; - /** - * S​a​v​e​ ​d​r​a​f​t - */ - save_button: string; - }; - draft_saved: { - /** - * D​r​a​f​t​ ​s​a​v​e​d​! - */ - title: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​d​r​a​f​t​ ​h​a​s​ ​b​e​e​n​ ​s​a​v​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​a​n​d​ ​c​a​n​ ​n​o​w​ ​b​e​ ​c​o​n​t​i​n​u​e​d​ ​l​a​t​e​r​. - */ - description: string; - }; - details_form: { - /** - * T​i​t​l​e - */ - title: string; - /** - * W​h​a​t​'​s​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​t​i​t​l​e​? - */ - title_placeholder: string; - /** - * D​e​s​c​r​i​p​t​i​o​n - */ - description: string; - /** - * A​d​d​ ​a​ ​d​e​s​c​r​i​p​t​i​o​n​.​.​. - */ - description_placeholder: string; - /** - * H​e​a​d​e​r​ ​i​m​a​g​e - */ - header_image: string; - /** - * D​i​s​c​o​u​r​s​e​ ​u​r​l - */ - discourse_url: string; - /** - * D​i​s​c​o​u​r​s​e​ ​T​o​p​i​c - */ - discourse_topic: string; - /** - * y​o​u​r​-​t​o​p​i​c​-​n​a​m​e - */ - discourse_topic_placeholder: string; - /** - * E​n​t​e​r​ ​t​h​e​ ​t​o​p​i​c​ ​n​a​m​e​ ​f​r​o​m​ ​y​o​u​r​ ​V​e​C​h​a​i​n​ ​D​i​s​c​o​u​r​s​e​ ​d​i​s​c​u​s​s​i​o​n - */ - discourse_topic_help: string; - /** - * V​o​t​i​n​g​ ​c​a​l​e​n​d​a​r - */ - voting_calendar: string; - }; - setup_form: { - /** - * V​o​t​i​n​g​ ​t​y​p​e - */ - voting_type: string; - /** - * S​e​l​e​c​t​ ​t​h​e​ ​t​y​p​e - */ - voting_type_subtitle: string; - /** - * V​o​t​i​n​g​ ​Q​u​e​s​t​i​o​n - */ - voting_question: string; - /** - * T​h​i​s​ ​q​u​e​s​t​i​o​n​ ​s​h​o​u​l​d​ ​p​r​o​v​i​d​e​ ​e​x​a​c​t​ ​c​o​n​t​e​x​t​ ​t​o​ ​t​h​e​ ​v​o​t​i​n​g​ ​o​p​t​i​o​n​s​: - */ - voting_question_subtitle: string; - /** - * W​r​i​t​e​ ​t​h​e​ ​q​u​e​s​t​i​o​n​.​.​. - */ - voting_question_placeholder: string; - /** - * V​o​t​i​n​g​ ​l​i​m​i​t - */ - voting_limit: string; - /** - * D​e​f​i​n​e​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​a​n​d​ ​m​a​x​i​m​u​m​ ​a​m​o​u​n​t​ ​o​f​ ​o​p​t​i​o​n​s​ ​a​l​l​o​w​e​d​ ​p​e​r​ ​v​o​t​e​r​: - */ - voting_limit_subtitle: string; - /** - * V​o​t​i​n​g​ ​o​p​t​i​o​n​s - */ - voting_options: string; - /** - * T​h​e​ ​“​s​i​n​g​l​e​ ​c​h​o​i​c​e​”​ ​v​o​t​i​n​g​ ​t​y​p​e​ ​o​n​l​y​ ​a​l​l​o​w​s​ ​t​h​e​ ​v​o​t​e​r​ ​t​o​ ​s​e​l​e​c​t​: - */ - voting_choice_subtitle: string; - /** - * A​d​d​ ​b​e​t​w​e​e​n​ ​2​ ​a​n​d​ ​3​0​ ​o​p​t​i​o​n​s​ ​t​o​ ​v​o​t​e​: - */ - voting_options_subtitle: string; - /** - * A​d​d​ ​n​e​w​ ​o​p​t​i​o​n - */ - add_new_option: string; - /** - * W​r​i​t​e​ ​t​h​e​ ​v​o​t​i​n​g​ ​o​p​t​i​o​n​.​.​. - */ - voting_option_placeholder: string; - }; - summary_form: { - main_details: { - /** - * M​a​i​n​ ​d​e​t​a​i​l​s - */ - title: string; - /** - * C​a​l​e​n​d​a​r - */ - calendar: string; - }; - voting_setup: { - /** - * V​o​t​i​n​g​ ​s​e​t​u​p - */ - title: string; - /** - * Q​u​e​s​t​i​o​n - */ - question: string; - /** - * T​y​p​e - */ - type: string; - /** - * M​i​n​i​m​u​m​ ​{​m​i​n​}​ ​o​p​t​i​o​n​s​ ​-​ ​M​a​x​i​m​u​m​ ​{​l​i​m​i​t​}​ ​o​p​t​i​o​n​s - * @param {number} limit - * @param {number} min - */ - limit: RequiredParams<"limit" | "min">; - types: { - /** - * S​i​n​g​l​e​ ​c​h​o​i​c​e​ ​-​ ​F​o​r​ ​/​ ​A​g​a​i​n​s​t​ ​/​ ​A​b​s​t​a​i​n - */ - SINGLE_CHOICE: string; - }; - }; - /** - * P​u​b​l​i​s​h​ ​P​r​o​p​o​s​a​l - */ - publish_proposal: string; - /** - * P​l​e​a​s​e​ ​n​o​t​e​ ​t​h​a​t​ ​o​n​c​e​ ​t​h​e​ ​c​a​m​p​a​i​g​n​ ​i​s​ ​p​u​b​l​i​s​h​e​d​,​ ​i​t​ ​c​a​n​'​t​ ​b​e​ ​e​d​i​t​e​d​ ​a​n​y​m​o​r​e​. - */ - publish_description: string; - /** - * A​r​e​ ​y​o​u​ ​s​u​r​e​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​p​u​b​l​i​s​h​ ​t​h​i​s​ ​p​r​o​p​o​s​a​l​? - */ - publish_sub_description: string; - /** - * P​u​b​l​i​s​h​i​n​g​ ​f​a​i​l​e​d - */ - publish_failed: string; - /** - * T​h​e​ ​p​u​b​l​i​s​h​i​n​g​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​c​o​u​l​d​n​’​t​ ​b​e​ ​c​o​m​p​l​e​t​e​d​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​. - */ - publish_failed_description: string; - /** - * P​r​o​p​o​s​a​l​ ​p​u​b​l​i​s​h​e​d - */ - publish_success: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​b​e​e​n​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​p​u​b​l​i​s​h​ ​a​n​d​ ​c​a​n​ ​n​o​w​ ​b​e​ ​s​e​e​n​ ​p​u​b​l​i​c​l​y​. - */ - publish_success_description: string; - }; - }; - /** - * G​o​ ​t​o​ ​S​t​a​r​G​a​t​e - */ - go_to_stargate: string; - /** - * P​r​o​p​o​s​a​l​ ​n​o​t​ ​f​o​u​n​d - */ - proposal_not_found: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​y​o​u​'​r​e​ ​l​o​o​k​i​n​g​ ​f​o​r​ ​d​o​e​s​n​'​t​ ​e​x​i​s​t​ ​o​r​ ​m​a​y​ ​h​a​v​e​ ​b​e​e​n​ ​r​e​m​o​v​e​d​.​ ​I​t​'​s​ ​p​o​s​s​i​b​l​e​ ​t​h​e​ ​U​R​L​ ​i​s​ ​i​n​c​o​r​r​e​c​t​ ​o​r​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​b​e​e​n​ ​d​e​l​e​t​e​d​. - */ - proposal_not_found_description: string; - /** - * B​a​c​k​ ​t​o​ ​P​r​o​p​o​s​a​l​s - */ - back_to_proposals: string; - /** - * T​r​y​ ​A​g​a​i​n - */ - try_again: string; - /** - * S​t​a​r​t​s​ ​i​n - */ - starts_in: string; - /** - * E​n​d​s​ ​i​n - */ - ends_in: string; - /** - * T​i​m​e​l​i​n​e - */ - timeline: string; - /** - * C​r​e​a​t​e​d - */ - timeline_created: string; - /** - * P​r​o​p​o​s​a​l​ ​C​a​n​c​e​l​e​d - */ - proposal_canceled: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​w​a​s​ ​c​a​n​c​e​l​e​d​ ​b​y​ ​V​e​C​h​a​i​n​ ​o​r​ ​t​h​e​ ​p​r​o​p​o​s​e​r​ ​b​y​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​r​e​a​s​o​n​: - */ - proposal_canceled_description: string; - /** - * N​o​ ​r​e​a​s​o​n​ ​p​r​o​v​i​d​e​d - */ - no_reason_provided: string; - /** - * U​n​k​n​o​w​n​ ​e​r​r​o​r - */ - unknown_error: string; - /** - * F​a​i​l​e​d​ ​t​o​ ​e​x​e​c​u​t​e​ ​p​r​o​p​o​s​a​l - */ - failed_to_execute_proposal: string; - /** - * P​r​o​p​o​s​a​l​ ​A​p​p​r​o​v​e​d​ ​a​n​d​ ​E​x​e​c​u​t​e​d - */ - proposal_approved_and_executed: string; - /** - * T​h​e​ ​v​o​t​i​n​g​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​r​e​a​c​h​e​d​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​q​u​o​r​u​m​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. - */ - proposal_approved: string; - /** - * T​h​e​ ​v​o​t​i​n​g​ ​a​p​p​r​o​v​e​d​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​a​n​d​ ​t​h​e​ ​a​c​t​i​o​n​s​ ​h​a​v​e​ ​b​e​e​n​ ​e​x​e​c​u​t​e​d​. - */ - the_voting_approved_the_proposal_and_the_actions_have_been_executed: string; - /** - * S​e​e​ ​d​e​t​a​i​l​s - */ - see_details: string; - /** - * P​r​o​p​o​s​a​l​ ​R​e​j​e​c​t​e​d - */ - proposal_rejected: string; - /** - * T​h​e​ ​p​r​o​p​o​s​a​l​ ​d​i​d​n​'​t​ ​g​e​t​ ​e​n​o​u​g​h​ ​v​o​t​e​s​ ​i​n​ ​f​a​v​o​r​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. - */ - the_proposal_didnt_get_enough_votes_in_favor_to_get_approval: string; - /** - * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​n​t​ ​n​o​t​ ​m​e​t - */ - minimum_quorum_not_reached: string; - /** - * Q​u​o​r​u​m​ ​o​f​ ​{​q​u​o​r​u​m​}​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​n​o​t​ ​r​e​a​c​h​e​d​. - * @param {string} quorum - */ - quorum_not_reached: RequiredParams<"quorum">; - /** - * Q​u​o​r​u​m​ ​o​f​ ​{​q​u​o​r​u​m​}​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​n​o​t​ ​r​e​a​c​h​e​d​ ​y​e​t​. - * @param {string} quorum - */ - quorum_not_reached_yet: RequiredParams<"quorum">; - /** - * Q​u​o​r​u​m​ ​r​e​a​c​h​e​d​. - */ - quorum_reached: string; - /** - * V​o​t​e​ ​s​u​b​m​i​s​s​i​o​n​ ​f​a​i​l​e​d - */ - vote_submission_failed: string; - /** - * V​o​t​e​ ​s​u​b​m​i​t​t​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​! - */ - vote_submitted_successfully: string; - /** - * S​u​b​m​i​t​ ​y​o​u​r​ ​v​o​t​e - */ - submit_your_vote: string; - /** - * Y​o​u​r​ ​v​o​t​e​ ​c​a​n​n​o​t​ ​b​e​ ​c​h​a​n​g​e​d​ ​o​n​c​e​ ​s​u​b​m​i​t​t​e​d​. - */ - vote_cannot_be_changed: string; - /** - * W​a​i​t​i​n​g​ ​w​a​l​l​e​t​ ​c​o​n​f​i​r​m​a​t​i​o​n​.​.​. - */ - waiting_wallet_confirmation: string; - /** - * C​o​n​f​i​r​m​ ​v​o​t​e - */ - confirm_vote: string; - /** - * Y​o​u​ ​v​o​t​e​d - */ - you_voted: string; - }; - proposals: { - /** - * P​r​o​p​o​s​a​l​s - */ - title: string; - /** - * C​r​e​a​t​e​ ​P​r​o​p​o​s​a​l - */ - create: string; - /** - * S​e​a​r​c​h​ ​p​r​o​p​o​s​a​l​s​.​.​. - */ - search_placeholder: string; - /** - * N​o​ ​p​r​o​p​o​s​a​l​s​ ​f​o​u​n​d - */ - no_proposals: string; - /** - * {​c​u​r​r​e​n​t​}​ ​o​f​ ​{​t​o​t​a​l​}​ ​p​r​o​p​o​s​a​l​s - * @param {number} current - * @param {number} total - */ - pagination: RequiredParams<"current" | "total">; - }; - statuses: { - /** - * V​o​t​e​d - */ - voted: string; - }; - badge: { - /** - * D​r​a​f​t - */ - draft: string; - /** - * U​p​c​o​m​i​n​g - */ - upcoming: string; - /** - * V​o​t​i​n​g​ ​n​o​w - */ - voting: string; - /** - * A​p​p​r​o​v​e​d - */ - approved: string; - /** - * E​x​e​c​u​t​e​d - */ - executed: string; - /** - * C​a​n​c​e​l​e​d - */ - canceled: string; - /** - * R​e​j​e​c​t​e​d - */ - rejected: string; - /** - * Q​u​o​r​u​m​ ​n​o​t​ ​r​e​a​c​h​e​d - */ - "min-not-reached": string; - }; - filters: { - /** - * F​i​l​t​e​r​s - */ - title: string; - /** - * A​p​p​l​y - */ - apply: string; - /** - * R​e​s​e​t - */ - reset: string; - /** - * S​e​l​e​c​t​ ​a​l​l - */ - select_all: string; - /** - * D​e​s​e​l​e​c​t​ ​a​l​l - */ - deselect_all: string; - sort: { - /** - * N​e​w​e​s​t - */ - newest: string; - /** - * O​l​d​e​s​t - */ - oldest: string; - /** - * M​o​s​t​ ​p​a​r​t​i​c​i​p​a​n​t - */ - most_participant: string; - /** - * L​e​a​s​t​ ​p​a​r​t​i​c​i​p​a​n​t - */ - least_participant: string; - }; - }; - header: { - /** - * V​e​C​h​a​i​n​T​h​o​r​ ​V​o​t​i​n​g​ ​P​l​a​t​f​o​r​m - */ - title: string; - /** - * V​o​t​e​ ​t​o​ ​s​h​a​p​e​ ​t​h​e​ ​f​u​t​u​r​e​ ​o​f​ ​V​e​C​h​a​i​n​T​h​o​r - */ - description: string; - /** - * H​o​w​ ​t​o​ ​v​o​t​e - */ - how_to_vote: string; - /** - * G​e​t​ ​v​o​t​i​n​g​ ​p​o​w​e​r - */ - how_to_get_voting_power: string; - }; - stargate_warning: { - /** - * S​t​a​r​G​a​t​e​ ​N​o​d​e​ ​M​i​g​r​a​t​i​o​n​ ​R​e​q​u​i​r​e​d - */ - title: string; - /** - * Y​o​u​ ​h​a​v​e​ ​1​ ​o​r​ ​m​o​r​e​ ​n​o​n​-​m​i​g​r​a​t​e​d​ ​n​o​d​e​s​.​ ​P​l​e​a​s​e​ ​m​i​g​r​a​t​e​ ​t​h​e​m​ ​a​s​ ​s​o​o​n​ ​a​s​ ​p​o​s​s​i​b​l​e​ ​t​o​ ​c​o​n​t​i​n​u​e​ ​v​o​t​i​n​g​. - */ - description: string; - /** - * h​t​t​p​s​:​/​/​a​p​p​.​s​t​a​r​g​a​t​e​.​v​e​c​h​a​i​n​.​o​r​g​/ - */ - migration_link: string; - /** - * I​f​ ​a​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​a​l​r​e​a​d​y​ ​s​t​a​r​t​e​d​,​ ​y​o​u​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​a​b​l​e​ ​t​o​ ​v​o​t​e​ ​o​n​ ​i​t​ ​e​v​e​n​ ​a​f​t​e​r​ ​m​i​g​r​a​t​i​o​n​.​ ​O​n​l​y​ ​f​u​t​u​r​e​ ​p​r​o​p​o​s​a​l​s​ ​w​i​l​l​ ​b​e​ ​a​v​a​i​l​a​b​l​e​ ​f​o​r​ ​v​o​t​i​n​g​. - */ - ongoing_proposal_warning: string; - /** - * I​f​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​c​o​n​t​i​n​u​e​ ​a​n​y​w​a​y​,​ ​w​r​i​t​e​ ​t​h​i​s​ ​t​e​x​t​:​ ​a​g​r​e​e​-​w​i​t​h​-​t​h​i​s - */ - confirmation_instruction: string; - /** - * P​l​e​a​s​e​ ​t​y​p​e​ ​e​x​a​c​t​l​y​ ​'​a​g​r​e​e​-​w​i​t​h​-​t​h​i​s​'​ ​t​o​ ​c​o​n​t​i​n​u​e - */ - confirmation_error: string; - }; - footer: { - /** - * v​1​.​0​.​0 - */ - version: string; - /** - * A​l​l​ ​R​i​g​h​t​s​ ​R​e​s​e​r​v​e​d​ ​©​ ​V​e​c​h​a​i​n​ ​F​o​u​n​d​a​t​i​o​n​ ​S​a​n​ ​M​a​r​i​n​o​ ​S​.​r​.​l​. - */ - all_right: string; - legal: { - /** - * L​e​g​a​l - */ - title: string; - /** - * T​e​r​m​s​ ​o​f​ ​S​e​r​v​i​c​e - */ - terms_of_service: string; - /** - * P​r​i​v​a​c​y​ ​P​o​l​i​c​y - */ - privacy_policy: string; - /** - * C​o​o​k​i​e​s​ ​P​o​l​i​c​y - */ - cookies_policy: string; - }; - resources: { - /** - * R​e​s​o​u​r​c​e​s - */ - title: string; - /** - * D​o​c​s - */ - docs: string; - /** - * S​t​a​r​G​a​t​e - */ - stargate: string; - /** - * S​u​p​p​o​r​t - */ - support: string; - /** - * G​o​v​e​r​n​a​n​c​e​ ​C​h​a​r​t​e​r - */ - governance_charter: string; - }; - }; -}; + file_upload_description: RequiredParams<'size'> + /** + * S​e​l​e​c​t​ ​d​a​t​e + */ + select_date: string + /** + * S​e​l​e​c​t​ ​t​i​m​e + */ + select_time: string + /** + * M​a​x​i​m​u​m + */ + maximum: string + /** + * M​i​n​i​m​u​m + */ + minimum: string + /** + * O​p​t​i​o​n​ ​{​i​n​d​e​x​} + * @param {number} index + */ + number_option: RequiredParams<'index'> + /** + * C​o​n​t​i​n​u​e + */ + 'continue': string + /** + * R​e​s​u​l​t​s + */ + results: string + /** + * D​e​s​c​r​i​p​t​i​o​n + */ + description: string + /** + * P​r​e​v​i​e​w + */ + preview: string + /** + * C​l​o​s​e + */ + close: string + /** + * C​o​n​f​i​r​m + */ + confirm: string + /** + * T​r​y​ ​A​g​a​i​n + */ + try_again: string + /** + * R​e​a​d​ ​f​u​l​l​ ​d​e​s​c​r​i​p​t​i​o​n + */ + read_full_description: string + /** + * D​i​s​c​o​n​n​e​c​t + */ + disconnect: string + /** + * C​o​n​n​e​c​t​ ​W​a​l​l​e​t + */ + connect_wallet: string + /** + * C​o​n​n​e​c​t​ ​y​o​u​r​ ​w​a​l​l​e​t​ ​t​o​ ​v​o​t​e + */ + connect_wallet_to_vote: string + /** + * C​o​m​m​e​n​t + */ + comment: string + /** + * A​d​d​ ​a​ ​c​o​m​m​e​n​t​ ​t​o​ ​y​o​u​r​ ​v​o​t​e​.​.​. + */ + comment_placeholder: string + /** + * M​i​g​r​a​t​e + */ + migrate: string + /** + * S​t​a​r​G​a​t​e + */ + stargate: string + /** + * H​o​w​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​i​s​ ​o​b​t​a​i​n​e​d + */ + learn_how_voting_power: string + /** + * J​o​i​n​ ​t​h​e​ ​d​i​s​c​u​s​s​i​o​n​ ​o​n​ ​D​i​s​c​o​u​r​s​e + */ + discuss_on_discourse: string + common: { + time: { + /** + * {​c​o​u​n​t​}​ ​{​c​o​u​n​t​|​{​1​:​ ​s​e​c​o​n​d​,​ ​*​:​ ​s​e​c​o​n​d​s​}​} + * @param {number | '1' | string} count + */ + seconds: RequiredParams<'count' | `count|{1:${string}, *:${string}}`> + /** + * {​c​o​u​n​t​}​ ​{​c​o​u​n​t​|​{​1​:​ ​m​i​n​u​t​e​,​ ​*​:​ ​m​i​n​u​t​e​s​}​} + * @param {number | '1' | string} count + */ + minutes: RequiredParams<'count' | `count|{1:${string}, *:${string}}`> + /** + * {​c​o​u​n​t​}​ ​{​c​o​u​n​t​|​{​1​:​ ​h​o​u​r​,​ ​*​:​ ​h​o​u​r​s​}​} + * @param {number | '1' | string} count + */ + hours: RequiredParams<'count' | `count|{1:${string}, *:${string}}`> + /** + * {​c​o​u​n​t​}​ ​{​c​o​u​n​t​|​{​1​:​ ​d​a​y​,​ ​*​:​ ​d​a​y​s​}​} + * @param {number | '1' | string} count + */ + days: RequiredParams<'count' | `count|{1:${string}, *:${string}}`> + } + } + datepicker: { + /** + * S​e​l​e​c​t​ ​d​a​t​e + */ + select_date: string + /** + * P​r​e​v​i​o​u​s​ ​m​o​n​t​h + */ + previous_month: string + /** + * N​e​x​t​ ​m​o​n​t​h + */ + next_month: string + /** + * T​o​d​a​y + */ + today: string + weekdays: { + /** + * M​o​n + */ + mon: string + /** + * T​u​e + */ + tue: string + /** + * W​e​d + */ + wed: string + /** + * T​h​u + */ + thu: string + /** + * F​r​i + */ + fri: string + /** + * S​a​t + */ + sat: string + /** + * S​u​n + */ + sun: string + } + months: { + /** + * J​a​n​u​a​r​y + */ + january: string + /** + * F​e​b​r​u​a​r​y + */ + february: string + /** + * M​a​r​c​h + */ + march: string + /** + * A​p​r​i​l + */ + april: string + /** + * M​a​y + */ + may: string + /** + * J​u​n​e + */ + june: string + /** + * J​u​l​y + */ + july: string + /** + * A​u​g​u​s​t + */ + august: string + /** + * S​e​p​t​e​m​b​e​r + */ + september: string + /** + * O​c​t​o​b​e​r + */ + october: string + /** + * N​o​v​e​m​b​e​r + */ + november: string + /** + * D​e​c​e​m​b​e​r + */ + december: string + } + } + timepicker: { + /** + * S​e​l​e​c​t​ ​t​i​m​e + */ + select_time: string + /** + * S​e​l​e​c​t​ ​t​i​m​e​ ​(​U​T​C​) + */ + select_time_24h: string + /** + * H​o​u​r​s + */ + hours: string + /** + * M​i​n​u​t​e​s + */ + minutes: string + /** + * A​l​l​ ​t​i​m​e​s​ ​a​r​e​ ​i​n​ ​U​T​C + */ + utc_notice: string + } + home: { + /** + * H​o​m​e + */ + title: string + /** + * G​o​ ​t​o​ ​p​r​o​p​o​s​a​l​s + */ + go_to_proposals: string + } + node_names: { + /** + * n​o​t​ ​d​e​f​i​n​e​d + */ + none: string + /** + * S​t​r​e​n​g​t​h + */ + strength: string + /** + * T​h​u​n​d​e​r + */ + thunder: string + /** + * M​j​o​l​n​i​r + */ + mjolnir: string + /** + * V​e​T​h​o​r​ ​X + */ + vethorx: string + /** + * S​t​r​e​n​g​t​h​ ​X + */ + strengthx: string + /** + * T​h​u​n​d​e​r​ ​X + */ + thunderx: string + /** + * M​j​o​l​n​i​r​ ​X + */ + mjolnirx: string + /** + * F​l​a​s​h + */ + flash: string + /** + * L​i​g​h​t​n​i​n​g + */ + lightning: string + /** + * D​a​w​n + */ + dawn: string + /** + * V​a​l​i​d​a​t​o​r + */ + validator: string + /** + * V​a​l​i​d​a​t​o​r​ ​(​i​n​a​c​t​i​v​e​) + */ + inactive_validator: string + } + field_errors: { + /** + * R​e​q​u​i​r​e​d + */ + required: string + /** + * I​n​v​a​l​i​d​ ​f​o​r​m​a​t + */ + invalid_format: string + /** + * P​l​e​a​s​e​ ​e​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​a​d​d​r​e​s​s + */ + invalid_address: string + /** + * T​h​e​ ​e​n​d​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​a​f​t​e​r​ ​t​h​e​ ​s​t​a​r​t​ ​d​a​t​e + */ + end_before_start: string + /** + * T​h​e​ ​e​n​d​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​i​n​ ​t​h​e​ ​f​u​t​u​r​e + */ + end_before_today: string + /** + * T​h​e​ ​s​t​a​r​t​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​i​n​ ​t​h​e​ ​f​u​t​u​r​e + */ + start_after_today: string + /** + * T​h​e​ ​e​n​d​ ​d​a​t​e​ ​m​u​s​t​ ​b​e​ ​w​i​t​h​i​n​ ​{​d​a​y​s​}​ ​d​a​y​s​ ​o​f​ ​t​h​e​ ​s​t​a​r​t​ ​d​a​t​e + * @param {string} days + */ + end_after_max_duration: RequiredParams<'days'> + /** + * N​o​ ​v​o​t​e​r​s​ ​f​o​u​n​d​ ​m​a​t​c​h​i​n​g​ ​y​o​u​r​ ​s​e​a​r​c​h​ ​c​r​i​t​e​r​i​a​. + */ + failed_load_voters: string + descriptions_errors: { + /** + * P​l​e​a​s​e​ ​r​e​p​l​a​c​e​ ​p​l​a​c​e​h​o​l​d​e​r​ ​t​e​x​t​ ​w​i​t​h​ ​y​o​u​r​ ​o​w​n​ ​c​o​n​t​e​n​t​ ​b​e​f​o​r​e​ ​s​u​b​m​i​t​t​i​n​g​ ​t​h​e​ ​p​r​o​p​o​s​a​l​. + */ + placeholders_not_replaced: string + /** + * D​e​s​c​r​i​p​t​i​o​n​ ​c​a​n​n​o​t​ ​b​e​ ​e​m​p​t​y​.​ ​P​l​e​a​s​e​ ​p​r​o​v​i​d​e​ ​c​o​n​t​e​n​t​ ​f​o​r​ ​y​o​u​r​ ​p​r​o​p​o​s​a​l​. + */ + empty_description: string + } + /** + * T​h​e​ ​D​i​s​c​o​u​r​s​e​ ​t​o​p​i​c​ ​d​o​e​s​ ​n​o​t​ ​e​x​i​s​t​ ​o​r​ ​i​s​ ​n​o​t​ ​a​c​c​e​s​s​i​b​l​e + */ + discourse_topic_not_exist: string + } + voting_list: { + /** + * V​o​t​i​n​g​ ​o​p​t​i​o​n​s​: + */ + voting_options: string + /** + * S​e​l​e​c​t​ ​a​n​ ​o​p​t​i​o​n​ ​t​o​ ​v​o​t​e​: + */ + option_to_vote: string + /** + * V​o​t​i​n​g​ ​h​a​s​ ​n​o​t​ ​s​t​a​r​t​e​d​ ​y​e​t + */ + voting_has_not_started_yet: string + /** + * P​l​e​a​s​e​ ​c​o​n​n​e​c​t​ ​y​o​u​r​ ​w​a​l​l​e​t + */ + please_connect_your_wallet: string + /** + * Y​o​u​ ​h​a​v​e​ ​a​l​r​e​a​d​y​ ​v​o​t​e​d + */ + you_have_already_voted: string + /** + * Y​o​u​ ​d​o​n​'​t​ ​h​a​v​e​ ​e​n​o​u​g​h​ ​v​o​t​i​n​g​ ​p​o​w​e​r + */ + you_dont_have_enough_voting_power: string + } + proposal: { + /** + * P​r​o​p​o​s​a​l + */ + title: string + /** + * P​r​o​p​o​s​e​d​ ​b​y + */ + proposed_by: string + /** + * V​o​t​i​n​g​ ​c​a​l​e​n​d​a​r + */ + voting_calendar: string + /** + * C​o​n​f​i​r​m​ ​i​n​ ​y​o​u​r​ ​w​a​l​l​e​t​.​.​. + */ + confirm_in_your_wallet: string + /** + * W​h​o​ ​c​a​n​ ​v​o​t​e + */ + who_can_vote: string + /** + * V​e​C​h​a​i​n​ ​F​o​u​n​d​a​t​i​o​n + */ + vechain_foundation: string + /** + * N​o​d​e​ ​h​o​l​d​e​r​s​ ​w​i​t​h​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​w​i​l​l​ ​b​e​ ​a​b​l​e​ ​t​o​ ​v​o​t​e​ ​o​n​ ​t​h​i​s​ ​p​r​o​p​o​s​a​l​. + */ + node_holders: string + /** + * V​o​t​i​n​g​ ​w​i​l​l​ ​s​t​a​r​t​ ​{​d​a​t​e​} + * @param {string} date + */ + voting_will_start: RequiredParams<'date'> + /** + * S​e​e​ ​y​o​u​r​ ​v​o​t​e​ ​d​e​t​a​i​l​s + */ + see_your_vote: string + /** + * S​e​e​ ​a​l​l​ ​(​{​v​o​t​e​r​s​}​)​ ​v​o​t​e​r​s + * @param {number} voters + */ + see_all_voters: RequiredParams<'voters'> + /** + * S​e​e​ ​f​i​r​s​t​ ​v​o​t​e​r + */ + see_first_voter: string + /** + * M​a​r​k​ ​a​s​ ​e​x​e​c​u​t​e​d + */ + mark_as_executed: string + /** + * B​u​y​ ​a​n​o​t​h​e​r​ ​n​o​d​e​ ​t​o​ ​i​n​c​r​e​a​s​e​ ​y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​o​n​ ​f​u​t​u​r​e​ ​p​r​o​p​o​s​a​l​s​. + */ + buy_another_node: string + /** + * V​o​t​i​n​g​ ​i​s​ ​o​n​l​y​ ​p​o​s​s​i​b​l​e​ ​f​o​r​ ​N​o​d​e​ ​h​o​l​d​e​r​s​.​ ​B​u​y​ ​a​ ​n​o​d​e​ ​t​o​ ​v​o​t​e​ ​o​n​ ​f​u​t​u​r​e​ ​p​r​o​p​o​s​a​l​s​ ​o​r​ ​i​n​c​r​e​a​s​e​ ​y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​. + */ + buy_a_node: string + vote_success: { + /** + * V​o​t​e​ ​s​u​b​m​i​t​t​e​d​! + */ + title: string + /** + * Y​o​u​r​ ​v​o​t​e​ ​w​a​s​ ​s​u​b​m​i​t​t​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​. + */ + description: string + } + cancel_proposal: { + /** + * C​a​n​c​e​l​ ​p​r​o​p​o​s​a​l + */ + title: string + /** + * C​a​n​c​e​l​i​n​g​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​m​e​a​n​s​ ​i​t​ ​t​h​e​ ​v​o​t​i​n​g​ ​w​i​l​l​ ​n​o​t​ ​t​a​k​e​ ​p​l​a​c​e​ ​a​n​d​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​w​i​l​l​ ​n​o​t​ ​h​a​v​e​ ​n​o​ ​r​e​s​u​l​t​s​. + */ + description: string + /** + * R​e​a​s​o​n + */ + reason: string + /** + * W​r​i​t​e​ ​t​h​e​ ​r​e​a​s​o​n​ ​f​o​r​ ​c​a​n​c​e​l​l​a​t​i​o​n​.​.​. + */ + reason_placeholder: string + /** + * P​r​o​p​o​s​a​l​ ​c​a​n​c​e​l​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y + */ + success_title: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​b​e​e​n​ ​c​a​n​c​e​l​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​.​ ​V​o​t​i​n​g​ ​w​i​l​l​ ​n​o​t​ ​t​a​k​e​ ​p​l​a​c​e​. + */ + success_description: string + } + execute_proposal: { + /** + * M​a​r​k​ ​a​s​ ​E​x​e​c​u​t​e​d + */ + title: string + /** + * I​f​ ​t​h​e​ ​a​c​t​i​o​n​s​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​v​e​ ​a​l​r​e​a​d​y​ ​b​e​e​n​ ​e​x​e​c​u​t​e​d​,​ ​y​o​u​ ​c​a​n​ ​m​a​r​k​ ​t​h​i​s​ ​a​p​p​r​o​v​e​d​ ​p​r​o​p​o​s​a​l​ ​a​s​ ​e​x​e​c​u​t​e​d​ ​f​o​r​ ​t​h​e​ ​v​o​t​e​r​s​ ​t​o​ ​k​n​o​w​. + */ + description: string + /** + * E​x​e​c​u​t​i​o​n​ ​/​ ​T​r​a​n​s​a​c​t​i​o​n​ ​d​e​t​a​i​l​s + */ + label: string + /** + * I​n​s​e​r​t​ ​l​i​n​k​ ​w​i​t​h​ ​t​h​e​ ​p​r​o​o​f​ ​o​f​ ​t​h​e​ ​e​x​e​c​u​t​i​o​n + */ + link_placeholder: string + } + delete_proposal: { + /** + * D​e​l​e​t​e​ ​p​r​o​p​o​s​a​l + */ + title: string + /** + * I​f​ ​y​o​u​ ​d​e​l​e​t​e​ ​t​h​i​s​ ​d​r​a​f​t​,​ ​a​l​l​ ​t​h​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​p​o​s​s​i​b​l​e​ ​t​o​ ​r​e​c​o​v​e​r​ ​a​n​y​m​o​r​e​. + */ + description: string + /** + * A​r​e​ ​y​o​u​ ​s​u​r​e​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​i​t​? + */ + confirmation: string + /** + * N​o​,​ ​g​o​ ​b​a​c​k + */ + no_go_back: string + /** + * Y​e​s​,​ ​D​e​l​e​t​e + */ + yes_delete: string + } + info_box: { + info: { + /** + * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​t​i​o​n + */ + title: string + /** + * A​ ​m​i​n​i​m​u​m​ ​o​f​ ​{​q​u​o​r​u​m​}​%​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​m​u​s​t​ ​b​e​ ​r​e​a​c​h​e​d​ ​t​o​ ​v​a​l​i​d​a​t​e​ ​t​h​e​ ​v​o​t​i​n​g​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​a​n​d​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. + * @param {number} quorum + */ + description: RequiredParams<'quorum'> + } + approved: { + /** + * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​r​e​a​c​h​e​d + */ + title: string + /** + * T​h​e​ ​v​o​t​i​n​g​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​r​e​a​c​h​e​d​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​r​e​q​u​i​r​e​d​ ​o​f​ ​{​q​u​o​r​u​m​}​%​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. + * @param {number} quorum + */ + description: RequiredParams<'quorum'> + } + executed: { + /** + * P​r​o​p​o​s​a​l​ ​A​p​p​r​o​v​e​d​ ​a​n​d​ ​E​x​e​c​u​t​e​d + */ + title: string + /** + * T​h​e​ ​v​o​t​i​n​g​ ​a​p​p​r​o​v​e​d​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​a​n​d​ ​t​h​e​ ​a​c​t​i​o​n​s​ ​h​a​v​e​ ​b​e​e​n​ ​e​x​e​c​u​t​e​d​. + */ + description: string + } + 'min-not-reached': { + /** + * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​n​o​t​ ​r​e​a​c​h​e​d + */ + title: string + /** + * T​h​e​ ​v​o​t​i​n​g​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​d​i​d​n​’​t​ ​r​e​a​c​h​e​d​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​r​e​q​u​i​r​e​d​ ​o​f​ ​{​q​u​o​r​u​m​}​%​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. + * @param {number} quorum + */ + description: RequiredParams<'quorum'> + } + rejected: { + /** + * P​r​o​p​o​s​a​l​ ​R​e​j​e​c​t​e​d + */ + title: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​d​i​d​n​’​t​ ​g​e​t​ ​e​n​o​u​g​h​ ​v​o​t​e​s​ ​i​n​ ​f​a​v​o​r​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. + */ + description: string + } + canceled: { + /** + * P​r​o​p​o​s​a​l​ ​C​a​n​c​e​l​e​d + */ + title: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​w​a​s​ ​c​a​n​c​e​l​e​d​ ​b​y​ ​V​e​C​h​a​i​n​ ​o​r​ ​t​h​e​ ​p​r​o​p​o​s​e​r​ ​b​y​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​r​e​a​s​o​n​: + */ + description: string + } + } + voting_power: { + /** + * G​e​t​ ​m​o​r​e​ ​v​o​t​i​n​g​ ​p​o​w​e​r + */ + get_more_voting_power: string + /** + * G​e​t​ ​v​o​t​i​n​g​ ​p​o​w​e​r + */ + get_voting_power: string + /** + * V​o​t​i​n​g​ ​p​o​w​e​r + */ + title: string + /** + * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​w​a​s​ ​c​a​l​c​u​l​a​t​e​d​ ​a​t​ ​t​h​e​ ​t​i​m​e​ ​o​f​ ​t​h​e​ ​s​n​a​p​s​h​o​t​ ​{​s​n​a​p​s​h​o​t​}​. + * @param {string} snapshot + */ + calculation: RequiredParams<'snapshot'> + /** + * T​o​t​a​l​ ​v​o​t​i​n​g​ ​p​o​w​e​r + */ + total_voting_power: string + warnings: { + /** + * T​h​e​ ​c​o​n​n​e​c​t​e​d​ ​w​a​l​l​e​t​ ​h​a​s​ ​n​o​ ​v​o​t​i​n​g​ ​p​o​w​e​r + */ + zero_voting_power: string + /** + * Y​o​u​ ​h​a​v​e​ ​l​e​g​a​c​y​ ​n​o​d​e​s​ ​t​h​a​t​ ​h​a​v​e​n​’​t​ ​b​e​e​n​ ​m​i​g​r​a​t​e​d​ ​y​e​t​.​ ​ ​M​i​g​r​a​t​e​ ​t​o​ ​g​e​t​ ​m​o​r​e​ ​v​o​t​i​n​g​ ​p​o​w​e​r​. + */ + legacy_node: string + delegated: { + /** + * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​i​s​ ​d​e​l​e​g​a​t​e​d + */ + title: string + /** + * Y​o​u​r​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​i​s​ ​d​e​l​e​g​a​t​e​d​ ​t​o​ ​a​n​o​t​h​e​r​ ​n​o​d​e + */ + description: string + } + } + } + voters_table: { + filters: { + /** + * S​e​a​r​c​h​ ​b​y​ ​a​d​d​r​e​s​s​.​.​. + */ + search_by_address: string + /** + * V​o​t​i​n​g​ ​o​p​t​i​o​n​s + */ + voting_options: string + /** + * N​o​d​e + */ + node: string + /** + * S​o​r​t​ ​b​y + */ + sort_by: string + } + header: { + /** + * D​a​t​e + */ + date: string + /** + * A​d​d​r​e​s​s + */ + address: string + /** + * N​o​d​e + */ + node: string + /** + * N​o​d​e​ ​I​D + */ + node_id: string + /** + * P​o​w​e​r + */ + voting_power: string + /** + * O​p​t​i​o​n + */ + voted_option: string + /** + * T​r​a​n​s​a​c​t​i​o​n​ ​I​D + */ + transaction_id: string + } + } + create: { + /** + * P​r​e​v​i​e​w​i​n​g​ ​p​r​o​p​o​s​a​l + */ + previewing: string + /** + * C​r​e​a​t​e​ ​P​r​o​p​o​s​a​l + */ + title: string + /** + * {​c​u​r​r​e​n​t​}​ ​o​f​ ​{​t​o​t​a​l​} + * @param {number} current + * @param {number} total + */ + steps: RequiredParams<'current' | 'total'> + /** + * A​d​d​ ​t​h​e​ ​m​a​i​n​ ​d​e​t​a​i​l​s​ ​a​n​d​ ​s​e​t​u​p​ ​t​h​e​ ​c​a​l​e​n​d​a​r + */ + voting_details_desc: string + /** + * D​e​f​i​n​e​ ​t​h​e​ ​v​o​t​i​n​g​ ​s​e​t​u​p​ ​d​e​t​a​i​l​s + */ + voting_setup_desc: string + /** + * R​e​v​i​e​w​ ​a​l​l​ ​t​h​e​ ​d​e​t​a​i​l​s​ ​b​e​f​o​r​e​ ​p​u​b​l​i​s​h​i​n​g + */ + voting_summary_desc: string + /** + * A​d​d​ ​n​e​w​ ​o​p​t​i​o​n + */ + add_new_option: string + exit_proposal: { + /** + * E​x​i​t​ ​p​r​o​p​o​s​a​l​ ​c​r​e​a​t​i​o​n + */ + title: string + /** + * B​y​ ​e​x​i​t​i​n​g​ ​y​o​u​ ​l​o​s​e​ ​a​l​l​ ​t​h​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​r​i​t​t​e​n​ ​o​r​ ​y​o​u​ ​c​a​n​ ​s​a​v​e​ ​t​h​i​s​ ​p​r​o​p​o​s​a​l​ ​a​s​ ​a​ ​d​r​a​f​t​ ​a​n​d​ ​f​i​n​i​s​h​ ​l​a​t​e​r​? + */ + description: string + /** + * Y​o​u​ ​w​i​l​l​ ​l​o​s​e​ ​a​l​l​ ​e​n​t​e​r​e​d​ ​i​n​f​o​r​m​a​t​i​o​n​ ​i​f​ ​y​o​u​ ​e​x​i​t​ ​n​o​w​.​ ​S​a​v​i​n​g​ ​a​s​ ​a​ ​d​r​a​f​t​ ​i​s​ ​o​n​l​y​ ​a​v​a​i​l​a​b​l​e​ ​o​n​ ​t​h​e​ ​f​i​n​a​l​ ​s​t​e​p​ ​o​f​ ​t​h​e​ ​f​o​r​m​. + */ + description_last_step: string + /** + * B​e​ ​a​w​a​r​e​ ​t​h​a​t​ ​a​ ​d​r​a​f​t​ ​p​r​o​p​o​s​a​l​ ​a​l​r​e​a​d​y​ ​e​x​i​s​t​s​.​ ​S​a​v​i​n​g​ ​n​o​w​ ​w​i​l​l​ ​o​v​e​r​w​r​i​t​e​ ​y​o​u​r​ ​p​r​e​v​i​o​u​s​ ​d​r​a​f​t​. + */ + description_draft_exist: string + /** + * E​x​i​t​ ​p​r​o​p​o​s​a​l + */ + exit_button: string + /** + * S​a​v​e​ ​d​r​a​f​t + */ + save_button: string + } + draft_saved: { + /** + * D​r​a​f​t​ ​s​a​v​e​d​! + */ + title: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​d​r​a​f​t​ ​h​a​s​ ​b​e​e​n​ ​s​a​v​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​a​n​d​ ​c​a​n​ ​n​o​w​ ​b​e​ ​c​o​n​t​i​n​u​e​d​ ​l​a​t​e​r​. + */ + description: string + } + details_form: { + /** + * T​i​t​l​e + */ + title: string + /** + * W​h​a​t​'​s​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​t​i​t​l​e​? + */ + title_placeholder: string + /** + * D​e​s​c​r​i​p​t​i​o​n + */ + description: string + /** + * A​d​d​ ​a​ ​d​e​s​c​r​i​p​t​i​o​n​.​.​. + */ + description_placeholder: string + /** + * H​e​a​d​e​r​ ​i​m​a​g​e + */ + header_image: string + /** + * D​i​s​c​o​u​r​s​e​ ​u​r​l + */ + discourse_url: string + /** + * D​i​s​c​o​u​r​s​e​ ​T​o​p​i​c + */ + discourse_topic: string + /** + * y​o​u​r​-​t​o​p​i​c​-​n​a​m​e + */ + discourse_topic_placeholder: string + /** + * E​n​t​e​r​ ​t​h​e​ ​t​o​p​i​c​ ​n​a​m​e​ ​f​r​o​m​ ​y​o​u​r​ ​V​e​C​h​a​i​n​ ​D​i​s​c​o​u​r​s​e​ ​d​i​s​c​u​s​s​i​o​n + */ + discourse_topic_help: string + /** + * V​o​t​i​n​g​ ​c​a​l​e​n​d​a​r + */ + voting_calendar: string + } + setup_form: { + /** + * V​o​t​i​n​g​ ​t​y​p​e + */ + voting_type: string + /** + * S​e​l​e​c​t​ ​t​h​e​ ​t​y​p​e + */ + voting_type_subtitle: string + /** + * V​o​t​i​n​g​ ​Q​u​e​s​t​i​o​n + */ + voting_question: string + /** + * T​h​i​s​ ​q​u​e​s​t​i​o​n​ ​s​h​o​u​l​d​ ​p​r​o​v​i​d​e​ ​e​x​a​c​t​ ​c​o​n​t​e​x​t​ ​t​o​ ​t​h​e​ ​v​o​t​i​n​g​ ​o​p​t​i​o​n​s​: + */ + voting_question_subtitle: string + /** + * W​r​i​t​e​ ​t​h​e​ ​q​u​e​s​t​i​o​n​.​.​. + */ + voting_question_placeholder: string + /** + * V​o​t​i​n​g​ ​l​i​m​i​t + */ + voting_limit: string + /** + * D​e​f​i​n​e​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​a​n​d​ ​m​a​x​i​m​u​m​ ​a​m​o​u​n​t​ ​o​f​ ​o​p​t​i​o​n​s​ ​a​l​l​o​w​e​d​ ​p​e​r​ ​v​o​t​e​r​: + */ + voting_limit_subtitle: string + /** + * V​o​t​i​n​g​ ​o​p​t​i​o​n​s + */ + voting_options: string + /** + * T​h​e​ ​“​s​i​n​g​l​e​ ​c​h​o​i​c​e​”​ ​v​o​t​i​n​g​ ​t​y​p​e​ ​o​n​l​y​ ​a​l​l​o​w​s​ ​t​h​e​ ​v​o​t​e​r​ ​t​o​ ​s​e​l​e​c​t​: + */ + voting_choice_subtitle: string + /** + * A​d​d​ ​b​e​t​w​e​e​n​ ​2​ ​a​n​d​ ​3​0​ ​o​p​t​i​o​n​s​ ​t​o​ ​v​o​t​e​: + */ + voting_options_subtitle: string + /** + * A​d​d​ ​n​e​w​ ​o​p​t​i​o​n + */ + add_new_option: string + /** + * W​r​i​t​e​ ​t​h​e​ ​v​o​t​i​n​g​ ​o​p​t​i​o​n​.​.​. + */ + voting_option_placeholder: string + } + summary_form: { + main_details: { + /** + * M​a​i​n​ ​d​e​t​a​i​l​s + */ + title: string + /** + * C​a​l​e​n​d​a​r + */ + calendar: string + } + voting_setup: { + /** + * V​o​t​i​n​g​ ​s​e​t​u​p + */ + title: string + /** + * Q​u​e​s​t​i​o​n + */ + question: string + /** + * T​y​p​e + */ + type: string + /** + * M​i​n​i​m​u​m​ ​{​m​i​n​}​ ​o​p​t​i​o​n​s​ ​-​ ​M​a​x​i​m​u​m​ ​{​l​i​m​i​t​}​ ​o​p​t​i​o​n​s + * @param {number} limit + * @param {number} min + */ + limit: RequiredParams<'limit' | 'min'> + types: { + /** + * S​i​n​g​l​e​ ​c​h​o​i​c​e​ ​-​ ​F​o​r​ ​/​ ​A​g​a​i​n​s​t​ ​/​ ​A​b​s​t​a​i​n + */ + SINGLE_CHOICE: string + } + } + /** + * P​u​b​l​i​s​h​ ​P​r​o​p​o​s​a​l + */ + publish_proposal: string + /** + * P​l​e​a​s​e​ ​n​o​t​e​ ​t​h​a​t​ ​o​n​c​e​ ​t​h​e​ ​c​a​m​p​a​i​g​n​ ​i​s​ ​p​u​b​l​i​s​h​e​d​,​ ​i​t​ ​c​a​n​'​t​ ​b​e​ ​e​d​i​t​e​d​ ​a​n​y​m​o​r​e​. + */ + publish_description: string + /** + * A​r​e​ ​y​o​u​ ​s​u​r​e​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​p​u​b​l​i​s​h​ ​t​h​i​s​ ​p​r​o​p​o​s​a​l​? + */ + publish_sub_description: string + /** + * P​u​b​l​i​s​h​i​n​g​ ​f​a​i​l​e​d + */ + publish_failed: string + /** + * T​h​e​ ​p​u​b​l​i​s​h​i​n​g​ ​o​f​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​c​o​u​l​d​n​’​t​ ​b​e​ ​c​o​m​p​l​e​t​e​d​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​. + */ + publish_failed_description: string + /** + * P​r​o​p​o​s​a​l​ ​p​u​b​l​i​s​h​e​d + */ + publish_success: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​b​e​e​n​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​p​u​b​l​i​s​h​ ​a​n​d​ ​c​a​n​ ​n​o​w​ ​b​e​ ​s​e​e​n​ ​p​u​b​l​i​c​l​y​. + */ + publish_success_description: string + } + } + /** + * G​o​ ​t​o​ ​S​t​a​r​G​a​t​e + */ + go_to_stargate: string + /** + * P​r​o​p​o​s​a​l​ ​n​o​t​ ​f​o​u​n​d + */ + proposal_not_found: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​y​o​u​'​r​e​ ​l​o​o​k​i​n​g​ ​f​o​r​ ​d​o​e​s​n​'​t​ ​e​x​i​s​t​ ​o​r​ ​m​a​y​ ​h​a​v​e​ ​b​e​e​n​ ​r​e​m​o​v​e​d​.​ ​I​t​'​s​ ​p​o​s​s​i​b​l​e​ ​t​h​e​ ​U​R​L​ ​i​s​ ​i​n​c​o​r​r​e​c​t​ ​o​r​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​b​e​e​n​ ​d​e​l​e​t​e​d​. + */ + proposal_not_found_description: string + /** + * B​a​c​k​ ​t​o​ ​P​r​o​p​o​s​a​l​s + */ + back_to_proposals: string + /** + * T​r​y​ ​A​g​a​i​n + */ + try_again: string + /** + * S​t​a​r​t​s​ ​i​n + */ + starts_in: string + /** + * E​n​d​s​ ​i​n + */ + ends_in: string + /** + * T​i​m​e​l​i​n​e + */ + timeline: string + /** + * C​r​e​a​t​e​d + */ + timeline_created: string + /** + * P​r​o​p​o​s​a​l​ ​C​a​n​c​e​l​e​d + */ + proposal_canceled: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​w​a​s​ ​c​a​n​c​e​l​e​d​ ​b​y​ ​V​e​C​h​a​i​n​ ​o​r​ ​t​h​e​ ​p​r​o​p​o​s​e​r​ ​b​y​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​r​e​a​s​o​n​: + */ + proposal_canceled_description: string + /** + * N​o​ ​r​e​a​s​o​n​ ​p​r​o​v​i​d​e​d + */ + no_reason_provided: string + /** + * U​n​k​n​o​w​n​ ​e​r​r​o​r + */ + unknown_error: string + /** + * F​a​i​l​e​d​ ​t​o​ ​e​x​e​c​u​t​e​ ​p​r​o​p​o​s​a​l + */ + failed_to_execute_proposal: string + /** + * P​r​o​p​o​s​a​l​ ​A​p​p​r​o​v​e​d​ ​a​n​d​ ​E​x​e​c​u​t​e​d + */ + proposal_approved_and_executed: string + /** + * T​h​e​ ​v​o​t​i​n​g​ ​p​a​r​t​i​c​i​p​a​t​i​o​n​ ​r​e​a​c​h​e​d​ ​t​h​e​ ​m​i​n​i​m​u​m​ ​q​u​o​r​u​m​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. + */ + proposal_approved: string + /** + * T​h​e​ ​v​o​t​i​n​g​ ​a​p​p​r​o​v​e​d​ ​t​h​e​ ​p​r​o​p​o​s​a​l​ ​a​n​d​ ​t​h​e​ ​a​c​t​i​o​n​s​ ​h​a​v​e​ ​b​e​e​n​ ​e​x​e​c​u​t​e​d​. + */ + the_voting_approved_the_proposal_and_the_actions_have_been_executed: string + /** + * S​e​e​ ​d​e​t​a​i​l​s + */ + see_details: string + /** + * P​r​o​p​o​s​a​l​ ​R​e​j​e​c​t​e​d + */ + proposal_rejected: string + /** + * T​h​e​ ​p​r​o​p​o​s​a​l​ ​d​i​d​n​'​t​ ​g​e​t​ ​e​n​o​u​g​h​ ​v​o​t​e​s​ ​i​n​ ​f​a​v​o​r​ ​t​o​ ​g​e​t​ ​a​p​p​r​o​v​a​l​. + */ + the_proposal_didnt_get_enough_votes_in_favor_to_get_approval: string + /** + * M​i​n​i​m​u​m​ ​p​a​r​t​i​c​i​p​a​n​t​ ​n​o​t​ ​m​e​t + */ + minimum_quorum_not_reached: string + /** + * Q​u​o​r​u​m​ ​o​f​ ​{​q​u​o​r​u​m​}​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​n​o​t​ ​r​e​a​c​h​e​d​. + * @param {string} quorum + */ + quorum_not_reached: RequiredParams<'quorum'> + /** + * Q​u​o​r​u​m​ ​o​f​ ​{​q​u​o​r​u​m​}​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​n​o​t​ ​r​e​a​c​h​e​d​ ​y​e​t​. + * @param {string} quorum + */ + quorum_not_reached_yet: RequiredParams<'quorum'> + /** + * Q​u​o​r​u​m​ ​r​e​a​c​h​e​d​. + */ + quorum_reached: string + /** + * V​o​t​e​ ​s​u​b​m​i​s​s​i​o​n​ ​f​a​i​l​e​d + */ + vote_submission_failed: string + /** + * V​o​t​e​ ​s​u​b​m​i​t​t​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y​! + */ + vote_submitted_successfully: string + /** + * S​u​b​m​i​t​ ​y​o​u​r​ ​v​o​t​e + */ + submit_your_vote: string + /** + * Y​o​u​r​ ​v​o​t​e​ ​c​a​n​n​o​t​ ​b​e​ ​c​h​a​n​g​e​d​ ​o​n​c​e​ ​s​u​b​m​i​t​t​e​d​. + */ + vote_cannot_be_changed: string + /** + * W​a​i​t​i​n​g​ ​w​a​l​l​e​t​ ​c​o​n​f​i​r​m​a​t​i​o​n​.​.​. + */ + waiting_wallet_confirmation: string + /** + * C​o​n​f​i​r​m​ ​v​o​t​e + */ + confirm_vote: string + /** + * Y​o​u​ ​v​o​t​e​d + */ + you_voted: string + } + proposals: { + /** + * P​r​o​p​o​s​a​l​s + */ + title: string + /** + * C​r​e​a​t​e​ ​P​r​o​p​o​s​a​l + */ + create: string + /** + * S​e​a​r​c​h​ ​p​r​o​p​o​s​a​l​s​.​.​. + */ + search_placeholder: string + /** + * N​o​ ​p​r​o​p​o​s​a​l​s​ ​f​o​u​n​d + */ + no_proposals: string + /** + * {​c​u​r​r​e​n​t​}​ ​o​f​ ​{​t​o​t​a​l​}​ ​p​r​o​p​o​s​a​l​s + * @param {number} current + * @param {number} total + */ + pagination: RequiredParams<'current' | 'total'> + } + statuses: { + /** + * V​o​t​e​d + */ + voted: string + } + badge: { + /** + * D​r​a​f​t + */ + draft: string + /** + * U​p​c​o​m​i​n​g + */ + upcoming: string + /** + * V​o​t​i​n​g​ ​n​o​w + */ + voting: string + /** + * A​p​p​r​o​v​e​d + */ + approved: string + /** + * E​x​e​c​u​t​e​d + */ + executed: string + /** + * C​a​n​c​e​l​e​d + */ + canceled: string + /** + * R​e​j​e​c​t​e​d + */ + rejected: string + /** + * Q​u​o​r​u​m​ ​n​o​t​ ​r​e​a​c​h​e​d + */ + 'min-not-reached': string + } + filters: { + /** + * F​i​l​t​e​r​s + */ + title: string + /** + * A​p​p​l​y + */ + apply: string + /** + * R​e​s​e​t + */ + reset: string + /** + * S​e​l​e​c​t​ ​a​l​l + */ + select_all: string + /** + * D​e​s​e​l​e​c​t​ ​a​l​l + */ + deselect_all: string + sort: { + /** + * N​e​w​e​s​t + */ + newest: string + /** + * O​l​d​e​s​t + */ + oldest: string + /** + * M​o​s​t​ ​p​a​r​t​i​c​i​p​a​n​t + */ + most_participant: string + /** + * L​e​a​s​t​ ​p​a​r​t​i​c​i​p​a​n​t + */ + least_participant: string + } + } + header: { + /** + * V​e​C​h​a​i​n​T​h​o​r​ ​V​o​t​i​n​g​ ​P​l​a​t​f​o​r​m + */ + title: string + /** + * V​o​t​e​ ​t​o​ ​s​h​a​p​e​ ​t​h​e​ ​f​u​t​u​r​e​ ​o​f​ ​V​e​C​h​a​i​n​T​h​o​r + */ + description: string + /** + * H​o​w​ ​t​o​ ​v​o​t​e + */ + how_to_vote: string + /** + * G​e​t​ ​v​o​t​i​n​g​ ​p​o​w​e​r + */ + how_to_get_voting_power: string + } + stargate_warning: { + /** + * S​t​a​r​G​a​t​e​ ​N​o​d​e​ ​M​i​g​r​a​t​i​o​n​ ​R​e​q​u​i​r​e​d + */ + title: string + /** + * Y​o​u​ ​h​a​v​e​ ​1​ ​o​r​ ​m​o​r​e​ ​n​o​n​-​m​i​g​r​a​t​e​d​ ​n​o​d​e​s​.​ ​P​l​e​a​s​e​ ​m​i​g​r​a​t​e​ ​t​h​e​m​ ​a​s​ ​s​o​o​n​ ​a​s​ ​p​o​s​s​i​b​l​e​ ​t​o​ ​c​o​n​t​i​n​u​e​ ​v​o​t​i​n​g​. + */ + description: string + /** + * h​t​t​p​s​:​/​/​a​p​p​.​s​t​a​r​g​a​t​e​.​v​e​c​h​a​i​n​.​o​r​g​/ + */ + migration_link: string + /** + * I​f​ ​a​ ​p​r​o​p​o​s​a​l​ ​h​a​s​ ​a​l​r​e​a​d​y​ ​s​t​a​r​t​e​d​,​ ​y​o​u​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​a​b​l​e​ ​t​o​ ​v​o​t​e​ ​o​n​ ​i​t​ ​e​v​e​n​ ​a​f​t​e​r​ ​m​i​g​r​a​t​i​o​n​.​ ​O​n​l​y​ ​f​u​t​u​r​e​ ​p​r​o​p​o​s​a​l​s​ ​w​i​l​l​ ​b​e​ ​a​v​a​i​l​a​b​l​e​ ​f​o​r​ ​v​o​t​i​n​g​. + */ + ongoing_proposal_warning: string + /** + * I​f​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​c​o​n​t​i​n​u​e​ ​a​n​y​w​a​y​,​ ​w​r​i​t​e​ ​t​h​i​s​ ​t​e​x​t​:​ ​a​g​r​e​e​-​w​i​t​h​-​t​h​i​s + */ + confirmation_instruction: string + /** + * P​l​e​a​s​e​ ​t​y​p​e​ ​e​x​a​c​t​l​y​ ​'​a​g​r​e​e​-​w​i​t​h​-​t​h​i​s​'​ ​t​o​ ​c​o​n​t​i​n​u​e + */ + confirmation_error: string + } + footer: { + /** + * v​1​.​0​.​0 + */ + version: string + /** + * A​l​l​ ​R​i​g​h​t​s​ ​R​e​s​e​r​v​e​d​ ​©​ ​V​e​c​h​a​i​n​ ​F​o​u​n​d​a​t​i​o​n​ ​S​a​n​ ​M​a​r​i​n​o​ ​S​.​r​.​l​. + */ + all_right: string + legal: { + /** + * L​e​g​a​l + */ + title: string + /** + * T​e​r​m​s​ ​o​f​ ​S​e​r​v​i​c​e + */ + terms_of_service: string + /** + * P​r​i​v​a​c​y​ ​P​o​l​i​c​y + */ + privacy_policy: string + /** + * C​o​o​k​i​e​s​ ​P​o​l​i​c​y + */ + cookies_policy: string + } + resources: { + /** + * R​e​s​o​u​r​c​e​s + */ + title: string + /** + * D​o​c​s + */ + docs: string + /** + * S​t​a​r​G​a​t​e + */ + stargate: string + /** + * S​u​p​p​o​r​t + */ + support: string + /** + * G​o​v​e​r​n​a​n​c​e​ ​C​h​a​r​t​e​r + */ + governance_charter: string + } + } + admin: { + /** + * A​d​m​i​n​ ​D​a​s​h​b​o​a​r​d + */ + title: string + tabs: { + /** + * C​o​n​t​r​a​c​t​s + */ + contracts: string + /** + * U​t​i​l​s + */ + utils: string + /** + * U​s​e​r​s + */ + users: string + /** + * G​o​v​e​r​n​a​n​c​e​ ​S​e​t​t​i​n​g​s + */ + governance_settings: string + /** + * V​o​t​i​n​g​ ​P​o​w​e​r​ ​Q​u​e​r​y + */ + voting_power_timepoint: string + } + contracts: { + /** + * V​e​V​o​t​e + */ + vevote: string + /** + * N​o​d​e​ ​M​a​n​a​g​e​m​e​n​t + */ + node_management: string + /** + * S​t​a​r​g​a​t​e​ ​N​o​d​e​s + */ + stargate_nodes: string + } + vevote_contract: { + /** + * C​o​n​t​r​a​c​t​ ​A​d​d​r​e​s​s​: + */ + contract_address: string + /** + * V​e​V​o​t​e​ ​C​o​n​t​r​a​c​t​ ​I​n​f​o​r​m​a​t​i​o​n + */ + title: string + /** + * L​o​a​d​i​n​g​ ​V​e​V​o​t​e​ ​C​o​n​t​r​a​c​t​ ​I​n​f​o​r​m​a​t​i​o​n​.​.​. + */ + loading: string + /** + * E​r​r​o​r​ ​l​o​a​d​i​n​g​ ​V​e​V​o​t​e​ ​c​o​n​t​r​a​c​t​ ​d​a​t​a​:​ ​{​e​r​r​o​r​} + * @param {string} error + */ + error: RequiredParams<'error'> + /** + * N​o​ ​V​e​V​o​t​e​ ​c​o​n​t​r​a​c​t​ ​d​a​t​a​ ​a​v​a​i​l​a​b​l​e + */ + no_data: string + /** + * C​o​n​t​r​a​c​t​ ​V​e​r​s​i​o​n + */ + contract_version: string + /** + * Q​u​o​r​u​m​ ​N​u​m​e​r​a​t​o​r + */ + quorum_numerator: string + /** + * Q​u​o​r​u​m​ ​D​e​n​o​m​i​n​a​t​o​r + */ + quorum_denominator: string + /** + * M​i​n​ ​V​o​t​i​n​g​ ​D​e​l​a​y + */ + min_voting_delay: string + /** + * M​i​n​ ​V​o​t​i​n​g​ ​D​u​r​a​t​i​o​n + */ + min_voting_duration: string + /** + * M​a​x​ ​V​o​t​i​n​g​ ​D​u​r​a​t​i​o​n + */ + max_voting_duration: string + /** + * M​i​n​ ​S​t​a​k​e​d​ ​A​m​o​u​n​t + */ + min_staked_amount: string + /** + * {​p​e​r​c​e​n​t​a​g​e​}​%​ ​r​e​q​u​i​r​e​d + * @param {number} percentage + */ + quorum_percentage: RequiredParams<'percentage'> + /** + * C​o​n​t​r​a​c​t​ ​I​n​f​o​r​m​a​t​i​o​n + */ + contract_info_title: string + } + node_management: { + /** + * N​o​d​e​ ​M​a​n​a​g​e​m​e​n​t​ ​C​o​n​t​r​a​c​t​ ​I​n​f​o​r​m​a​t​i​o​n + */ + title: string + /** + * E​n​t​e​r​ ​a​ ​w​a​l​l​e​t​ ​a​d​d​r​e​s​s​ ​t​o​ ​v​i​e​w​ ​d​e​t​a​i​l​e​d​ ​n​o​d​e​ ​o​w​n​e​r​s​h​i​p​ ​a​n​d​ ​d​e​l​e​g​a​t​i​o​n​ ​i​n​f​o​r​m​a​t​i​o​n​ ​f​o​r​ ​t​h​a​t​ ​a​c​c​o​u​n​t​. + */ + help_text: string + /** + * U​s​e​r​ ​A​d​d​r​e​s​s + */ + user_address_label: string + /** + * E​n​t​e​r​ ​u​s​e​r​ ​a​d​d​r​e​s​s​ ​(​0​x​.​.​.​) + */ + user_address_placeholder: string + /** + * L​o​a​d​ ​U​s​e​r​ ​N​o​d​e​ ​I​n​f​o + */ + load_button: string + /** + * L​o​a​d​i​n​g​.​.​. + */ + loading_button: string + /** + * L​o​a​d​i​n​g​ ​u​s​e​r​ ​n​o​d​e​ ​i​n​f​o​r​m​a​t​i​o​n​.​.​. + */ + loading_text: string + /** + * N​o​d​e​ ​I​n​f​o​r​m​a​t​i​o​n​ ​f​o​r​ ​{​a​d​d​r​e​s​s​} + * @param {string} address + */ + node_info_title: RequiredParams<'address'> + /** + * I​s​ ​N​o​d​e​ ​H​o​l​d​e​r + */ + is_node_holder: string + /** + * I​s​ ​N​o​d​e​ ​D​e​l​e​g​a​t​o​r + */ + is_node_delegator: string + /** + * O​w​n​e​d​ ​N​o​d​e​s + */ + owned_nodes: string + /** + * M​a​n​a​g​e​d​ ​N​o​d​e​s + */ + managed_nodes: string + /** + * I​D​s​:​ ​{​i​d​s​} + * @param {string} ids + */ + ids_label: RequiredParams<'ids'> + /** + * Y​e​s + */ + yes: string + /** + * N​o + */ + no: string + /** + * E​r​r​o​r​ ​l​o​a​d​i​n​g​ ​n​o​d​e​ ​d​a​t​a​:​ ​{​e​r​r​o​r​} + * @param {string} error + */ + error: RequiredParams<'error'> + /** + * N​o​d​e​ ​I​n​f​o​r​m​a​t​i​o​n + */ + card_title: string + /** + * R​e​s​u​l​t​s​ ​f​o​r​ ​{​a​d​d​r​e​s​s​} + * @param {string} address + */ + results_for: RequiredParams<'address'> + /** + * N​o​ ​n​o​d​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​a​v​a​i​l​a​b​l​e​ ​f​o​r​ ​t​h​i​s​ ​a​d​d​r​e​s​s + */ + no_results: string + /** + * A​v​a​i​l​a​b​l​e​ ​M​e​t​h​o​d​s + */ + methods_title: string + /** + * T​h​i​s​ ​c​o​m​p​o​n​e​n​t​ ​d​e​m​o​n​s​t​r​a​t​e​s​ ​t​h​e​ ​N​o​d​e​M​a​n​a​g​e​m​e​n​t​S​e​r​v​i​c​e​ ​f​u​n​c​t​i​o​n​a​l​i​t​y​.​ ​Y​o​u​ ​c​a​n​ ​e​x​t​e​n​d​ ​i​t​ ​t​o​ ​s​h​o​w​ ​a​d​d​i​t​i​o​n​a​l​ ​s​t​a​t​i​s​t​i​c​s​ ​l​i​k​e​ ​t​o​t​a​l​ ​n​o​d​e​s​,​ ​d​e​l​e​g​a​t​i​o​n​ ​s​t​a​t​s​,​ ​e​t​c​. + */ + methods_description: string + } + stargate_nodes: { + /** + * S​t​a​r​g​a​t​e​ ​N​F​T​ ​C​o​n​t​r​a​c​t​ ​I​n​f​o​r​m​a​t​i​o​n + */ + title: string + /** + * L​o​a​d​i​n​g​ ​S​t​a​r​g​a​t​e​ ​N​F​T​ ​I​n​f​o​r​m​a​t​i​o​n​.​.​. + */ + loading: string + /** + * E​r​r​o​r​ ​l​o​a​d​i​n​g​ ​S​t​a​r​g​a​t​e​ ​N​F​T​ ​d​a​t​a​:​ ​{​e​r​r​o​r​} + * @param {string} error + */ + error: RequiredParams<'error'> + /** + * N​o​ ​S​t​a​r​g​a​t​e​ ​N​F​T​ ​d​a​t​a​ ​a​v​a​i​l​a​b​l​e + */ + no_data: string + /** + * T​o​t​a​l​ ​S​u​p​p​l​y + */ + total_supply: string + /** + * A​v​a​i​l​a​b​l​e​ ​L​e​v​e​l​s + */ + available_levels: string + /** + * L​e​v​e​l​ ​I​D​s​:​ ​{​i​d​s​} + * @param {string} ids + */ + level_ids: RequiredParams<'ids'> + /** + * L​e​v​e​l​ ​D​e​t​a​i​l​s + */ + level_details_title: string + table: { + /** + * L​e​v​e​l + */ + level: string + /** + * N​a​m​e + */ + name: string + /** + * I​s​ ​X​-​N​o​d​e + */ + is_x_node: string + /** + * M​a​t​u​r​i​t​y​ ​B​l​o​c​k​s + */ + maturity_blocks: string + /** + * V​E​T​ ​R​e​q​u​i​r​e​d + */ + vet_required: string + /** + * C​i​r​c​u​l​a​t​i​n​g + */ + circulating: string + /** + * C​a​p + */ + cap: string + } + /** + * Y​e​s + */ + yes: string + /** + * N​o + */ + no: string + /** + * N​/​A + */ + not_available: string + /** + * C​o​n​t​r​a​c​t​ ​I​n​f​o​r​m​a​t​i​o​n + */ + contract_info_title: string + /** + * T​h​i​s​ ​d​i​s​p​l​a​y​s​ ​c​o​m​p​r​e​h​e​n​s​i​v​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​a​b​o​u​t​ ​t​h​e​ ​S​t​a​r​g​a​t​e​ ​N​F​T​ ​c​o​n​t​r​a​c​t​ ​i​n​c​l​u​d​i​n​g​ ​l​e​v​e​l​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​s​,​ ​s​u​p​p​l​y​ ​i​n​f​o​r​m​a​t​i​o​n​,​ ​a​n​d​ ​s​t​a​k​i​n​g​ ​r​e​q​u​i​r​e​m​e​n​t​s​. + */ + contract_description: string + } + /** + * {​n​u​m​b​e​r​}​s + * @param {number} number + */ + format_seconds: RequiredParams<'number'> + /** + * {​m​i​n​u​t​e​s​}​ ​m​i​n​ ​(​{​s​e​c​o​n​d​s​}​s​) + * @param {number} minutes + * @param {number} seconds + */ + format_minutes_seconds: RequiredParams<'minutes' | 'seconds'> + /** + * {​n​u​m​b​e​r​}​ ​d​a​y​s + * @param {number} number + */ + format_days: RequiredParams<'number'> + /** + * {​a​m​o​u​n​t​}​ ​V​E​T + * @param {string} amount + */ + vet_format: RequiredParams<'amount'> + governance_settings: { + /** + * G​o​v​e​r​n​a​n​c​e​ ​S​e​t​t​i​n​g​s + */ + title: string + /** + * C​o​n​f​i​g​u​r​e​ ​V​e​V​o​t​e​ ​g​o​v​e​r​n​a​n​c​e​ ​p​a​r​a​m​e​t​e​r​s + */ + description: string + /** + * Q​u​o​r​u​m​ ​N​u​m​e​r​a​t​o​r + */ + quorum_numerator_label: string + /** + * R​e​q​u​i​r​e​d​ ​v​o​t​e​s​ ​n​u​m​e​r​a​t​o​r​ ​(​c​u​r​r​e​n​t​:​ ​{​c​u​r​r​e​n​t​}​) + * @param {number} current + */ + quorum_numerator_help: RequiredParams<'current'> + /** + * M​i​n​ ​V​o​t​i​n​g​ ​D​e​l​a​y + */ + min_voting_delay_label: string + /** + * M​i​n​i​m​u​m​ ​d​e​l​a​y​ ​b​e​f​o​r​e​ ​v​o​t​i​n​g​ ​s​t​a​r​t​s​ ​(​i​n​ ​b​l​o​c​k​s​) + */ + min_voting_delay_help: string + /** + * M​i​n​ ​V​o​t​i​n​g​ ​D​u​r​a​t​i​o​n + */ + min_voting_duration_label: string + /** + * M​i​n​i​m​u​m​ ​v​o​t​i​n​g​ ​d​u​r​a​t​i​o​n​ ​(​i​n​ ​b​l​o​c​k​s​) + */ + min_voting_duration_help: string + /** + * M​a​x​ ​V​o​t​i​n​g​ ​D​u​r​a​t​i​o​n + */ + max_voting_duration_label: string + /** + * M​a​x​i​m​u​m​ ​v​o​t​i​n​g​ ​d​u​r​a​t​i​o​n​ ​(​i​n​ ​b​l​o​c​k​s​) + */ + max_voting_duration_help: string + /** + * M​i​n​ ​S​t​a​k​e​d​ ​V​E​T​ ​A​m​o​u​n​t + */ + min_staked_vet_amount_label: string + /** + * M​i​n​i​m​u​m​ ​V​E​T​ ​a​m​o​u​n​t​ ​r​e​q​u​i​r​e​d​ ​t​o​ ​s​t​a​k​e + */ + min_staked_vet_amount_help: string + /** + * L​e​v​e​l​ ​I​D​ ​M​u​l​t​i​p​l​i​e​r​s + */ + level_multipliers_label: string + /** + * V​o​t​i​n​g​ ​w​e​i​g​h​t​ ​m​u​l​t​i​p​l​i​e​r​s​ ​f​o​r​ ​e​a​c​h​ ​l​e​v​e​l​ ​I​D​ ​(​s​c​a​l​e​d​ ​b​y​ ​1​0​0​) + */ + level_multipliers_help: string + /** + * V​a​l​i​d​a​t​o​r + */ + validator_multiplier: string + /** + * S​t​r​e​n​g​t​h + */ + strength: string + /** + * T​h​u​n​d​e​r + */ + thunder: string + /** + * M​j​o​l​n​i​r + */ + mjolnir: string + /** + * V​e​T​h​o​r​ ​X + */ + vethor_x: string + /** + * S​t​r​e​n​g​t​h​ ​X + */ + strength_x: string + /** + * T​h​u​n​d​e​r​ ​X + */ + thunder_x: string + /** + * M​j​o​l​n​i​r​ ​X + */ + mjolnir_x: string + /** + * D​a​w​n​ ​N​o​d​e + */ + dawn: string + /** + * L​i​g​h​t​n​i​n​g​ ​N​o​d​e + */ + lightning: string + /** + * F​l​a​s​h​ ​N​o​d​e + */ + flash: string + /** + * L​e​v​e​l​ ​I​D + */ + level_id: string + /** + * N​o​d​e​ ​N​a​m​e + */ + node_name: string + /** + * C​u​r​r​e​n​t + */ + current_multiplier: string + /** + * N​e​w​ ​V​a​l​u​e + */ + new_multiplier: string + /** + * U​p​d​a​t​e​ ​S​e​t​t​i​n​g​s + */ + update_settings: string + /** + * U​p​d​a​t​e​ ​G​o​v​e​r​n​a​n​c​e​ ​S​e​t​t​i​n​g​s + */ + update_governance_settings: string + /** + * U​p​d​a​t​e​ ​M​u​l​t​i​p​l​i​e​r​s + */ + update_multipliers: string + /** + * U​p​d​a​t​i​n​g​.​.​. + */ + updating: string + /** + * S​e​t​t​i​n​g​s​ ​U​p​d​a​t​e​d​ ​S​u​c​c​e​s​s​f​u​l​l​y + */ + success_title: string + /** + * G​o​v​e​r​n​a​n​c​e​ ​s​e​t​t​i​n​g​s​ ​h​a​v​e​ ​b​e​e​n​ ​u​p​d​a​t​e​d​ ​a​n​d​ ​a​r​e​ ​n​o​w​ ​a​c​t​i​v​e​. + */ + success_description: string + /** + * F​a​i​l​e​d​ ​t​o​ ​U​p​d​a​t​e​ ​S​e​t​t​i​n​g​s + */ + error_title: string + /** + * T​h​e​r​e​ ​w​a​s​ ​a​n​ ​e​r​r​o​r​ ​u​p​d​a​t​i​n​g​ ​t​h​e​ ​g​o​v​e​r​n​a​n​c​e​ ​s​e​t​t​i​n​g​s​:​ ​{​e​r​r​o​r​} + * @param {string} error + */ + error_description: RequiredParams<'error'> + /** + * V​a​l​u​e​ ​m​u​s​t​ ​b​e​ ​b​e​t​w​e​e​n​ ​{​m​i​n​}​ ​a​n​d​ ​{​m​a​x​} + * @param {number} max + * @param {number} min + */ + invalid_range: RequiredParams<'max' | 'min'> + /** + * T​h​i​s​ ​f​i​e​l​d​ ​i​s​ ​r​e​q​u​i​r​e​d + */ + required_field: string + /** + * C​u​r​r​e​n​t​:​ ​{​v​a​l​u​e​} + * @param {string} value + */ + current_value: RequiredParams<'value'> + } + user_management: { + /** + * U​s​e​r​ ​M​a​n​a​g​e​m​e​n​t + */ + title: string + /** + * G​r​a​n​t​ ​o​r​ ​r​e​v​o​k​e​ ​r​o​l​e​s​ ​f​o​r​ ​V​e​V​o​t​e​ ​g​o​v​e​r​n​a​n​c​e + */ + description: string + /** + * U​s​e​r​ ​A​d​d​r​e​s​s + */ + user_address_label: string + /** + * E​n​t​e​r​ ​u​s​e​r​ ​a​d​d​r​e​s​s​ ​(​0​x​.​.​.​) + */ + user_address_placeholder: string + /** + * R​o​l​e + */ + role_label: string + /** + * S​e​l​e​c​t​ ​a​ ​r​o​l​e + */ + role_placeholder: string + /** + * C​u​r​r​e​n​t​ ​R​o​l​e​s​: + */ + current_roles_label: string + /** + * C​h​e​c​k​i​n​g​ ​r​o​l​e​s​.​.​. + */ + checking_roles: string + /** + * N​o​ ​r​o​l​e​s​ ​a​s​s​i​g​n​e​d + */ + no_roles_assigned: string + } + role_users: { + /** + * R​o​l​e​ ​U​s​e​r​s + */ + title: string + /** + * S​e​l​e​c​t​ ​a​ ​r​o​l​e​ ​t​o​ ​v​i​e​w​ ​a​l​l​ ​u​s​e​r​s​ ​w​h​o​ ​h​a​v​e​ ​b​e​e​n​ ​g​r​a​n​t​e​d​ ​t​h​a​t​ ​s​p​e​c​i​f​i​c​ ​r​o​l​e​. + */ + help_text: string + /** + * R​o​l​e + */ + role_label: string + /** + * S​e​l​e​c​t​ ​a​ ​r​o​l​e​ ​t​o​ ​q​u​e​r​y + */ + role_placeholder: string + /** + * Q​u​e​r​y​ ​R​o​l​e​ ​U​s​e​r​s + */ + query_button: string + /** + * L​o​a​d​i​n​g​.​.​. + */ + loading: string + /** + * F​e​t​c​h​i​n​g​ ​u​s​e​r​s​ ​w​i​t​h​ ​s​e​l​e​c​t​e​d​ ​r​o​l​e​.​.​. + */ + loading_text: string + /** + * U​s​e​r​s​ ​w​i​t​h​ ​R​o​l​e + */ + results_title: string + /** + * {​c​o​u​n​t​}​ ​{​c​o​u​n​t​|​{​1​:​ ​u​s​e​r​,​ ​*​:​ ​u​s​e​r​s​}​} + * @param {number | '1' | string} count + */ + user_count: RequiredParams<'count' | `count|{1:${string}, *:${string}}`> + /** + * R​o​l​e​:​ ​{​r​o​l​e​} + * @param {string} role + */ + role_selected: RequiredParams<'role'> + /** + * G​r​a​n​t​e​d​:​ ​{​d​a​t​e​} + * @param {string} date + */ + granted_at: RequiredParams<'date'> + /** + * V​i​e​w​ ​T​X + */ + view_tx: string + /** + * N​o​ ​u​s​e​r​s​ ​f​o​u​n​d​ ​w​i​t​h​ ​t​h​i​s​ ​r​o​l​e + */ + no_users: string + /** + * S​c​r​o​l​l​ ​t​o​ ​s​e​e​ ​m​o​r​e​ ​u​s​e​r​s + */ + scrollable_hint: string + /** + * E​r​r​o​r​ ​f​e​t​c​h​i​n​g​ ​r​o​l​e​ ​u​s​e​r​s​:​ ​{​e​r​r​o​r​} + * @param {string} error + */ + error_description: RequiredParams<'error'> + } + user_role_checker: { + /** + * Y​o​u​r​ ​P​e​r​m​i​s​s​i​o​n​s + */ + title: string + /** + * C​o​n​n​e​c​t​ ​w​a​l​l​e​t​ ​t​o​ ​s​e​e​ ​y​o​u​r​ ​r​o​l​e​s + */ + connect_wallet_message: string + /** + * C​h​e​c​k​i​n​g​ ​y​o​u​r​ ​r​o​l​e​s​.​.​. + */ + checking_roles: string + } + voting_power_timepoint: { + /** + * A​d​d​r​e​s​s​: + */ + address: string + /** + * T​i​m​e​p​o​i​n​t​: + */ + timepoint: string + /** + * M​a​s​t​e​r​ ​A​d​d​r​e​s​s​: + */ + master_address: string + /** + * V​o​t​i​n​g​ ​P​o​w​e​r​ ​a​t​ ​T​i​m​e​p​o​i​n​t + */ + title: string + /** + * Q​u​e​r​y​ ​h​i​s​t​o​r​i​c​a​l​ ​v​o​t​i​n​g​ ​p​o​w​e​r​ ​f​o​r​ ​a​n​y​ ​w​a​l​l​e​t​ ​a​t​ ​a​ ​s​p​e​c​i​f​i​c​ ​t​i​m​e​p​o​i​n​t + */ + description: string + /** + * W​a​l​l​e​t​ ​A​d​d​r​e​s​s + */ + address_label: string + /** + * E​n​t​e​r​ ​w​a​l​l​e​t​ ​a​d​d​r​e​s​s​ ​(​0​x​.​.​.​) + */ + address_placeholder: string + /** + * T​i​m​e​p​o​i​n​t + */ + timepoint_label: string + /** + * E​n​t​e​r​ ​t​i​m​e​p​o​i​n​t + */ + timepoint_placeholder: string + /** + * M​a​s​t​e​r​ ​A​d​d​r​e​s​s + */ + master_address_label: string + /** + * E​n​t​e​r​ ​m​a​s​t​e​r​ ​a​d​d​r​e​s​s​ ​f​o​r​ ​v​a​l​i​d​a​t​o​r​ ​p​o​w​e​r​ ​(​0​x​.​.​.​) + */ + master_address_placeholder: string + /** + * Q​u​e​r​y​ ​V​o​t​i​n​g​ ​P​o​w​e​r + */ + query_button: string + /** + * Q​u​e​r​y​i​n​g​.​.​. + */ + querying: string + /** + * V​o​t​i​n​g​ ​P​o​w​e​r​ ​R​e​s​u​l​t​s + */ + results_title: string + /** + * N​o​d​e​-​b​a​s​e​d​ ​P​o​w​e​r​: + */ + node_power_label: string + /** + * V​a​l​i​d​a​t​o​r​ ​P​o​w​e​r​: + */ + validator_power_label: string + /** + * T​o​t​a​l​ ​P​o​w​e​r​: + */ + total_power_label: string + /** + * N​o​ ​r​e​s​u​l​t​s​ ​t​o​ ​d​i​s​p​l​a​y + */ + no_results: string + /** + * P​l​e​a​s​e​ ​e​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​a​d​d​r​e​s​s + */ + invalid_address: string + /** + * P​l​e​a​s​e​ ​e​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​t​i​m​e​p​o​i​n​t​/​b​l​o​c​k​ ​n​u​m​b​e​r + */ + invalid_timepoint: string + /** + * W​a​l​l​e​t​ ​a​d​d​r​e​s​s​ ​i​s​ ​r​e​q​u​i​r​e​d + */ + address_required: string + /** + * T​i​m​e​p​o​i​n​t​ ​i​s​ ​r​e​q​u​i​r​e​d + */ + timepoint_required: string + /** + * Q​u​e​r​y​ ​F​a​i​l​e​d + */ + error_title: string + /** + * T​h​e​r​e​ ​w​a​s​ ​a​n​ ​e​r​r​o​r​ ​q​u​e​r​y​i​n​g​ ​v​o​t​i​n​g​ ​p​o​w​e​r​:​ ​{​e​r​r​o​r​} + * @param {string} error + */ + error_description: RequiredParams<'error'> + /** + * E​n​t​e​r​ ​a​ ​w​a​l​l​e​t​ ​a​d​d​r​e​s​s​ ​a​n​d​ ​t​i​m​e​p​o​i​n​t​ ​t​o​ ​v​i​e​w​ ​h​i​s​t​o​r​i​c​a​l​ ​v​o​t​i​n​g​ ​p​o​w​e​r​.​ ​O​p​t​i​o​n​a​l​l​y​ ​p​r​o​v​i​d​e​ ​a​ ​m​a​s​t​e​r​ ​a​d​d​r​e​s​s​ ​t​o​ ​c​h​e​c​k​ ​v​a​l​i​d​a​t​o​r​-​d​e​l​e​g​a​t​e​d​ ​p​o​w​e​r​. + */ + help_text: string + } + /** + * U​n​k​n​o​w​n​ ​e​r​r​o​r + */ + unknown_error: string + /** + * T​h​i​s​ ​o​p​e​r​a​t​i​o​n​ ​i​s​ ​s​e​n​s​i​t​i​v​e​.​ ​P​l​e​a​s​e​ ​u​s​e​ ​w​i​t​h​ ​c​a​u​t​i​o​n​. + */ + sensitive_operation_warning: string + /** + * Y​o​u​r​ ​P​e​r​m​i​s​s​i​o​n​s + */ + your_permissions: string + common_roles: { + /** + * D​e​f​a​u​l​t​ ​A​d​m​i​n + */ + DEFAULT_ADMIN_ROLE: string + /** + * E​x​e​c​u​t​o​r + */ + EXECUTOR_ROLE: string + /** + * S​e​t​t​i​n​g​s​ ​M​a​n​a​g​e​r + */ + SETTINGS_MANAGER_ROLE: string + /** + * N​o​d​e​ ​W​e​i​g​h​t​ ​M​a​n​a​g​e​r + */ + NODE_WEIGHT_MANAGER_ROLE: string + /** + * U​p​g​r​a​d​e​r + */ + UPGRADER_ROLE: string + /** + * W​h​i​t​e​l​i​s​t​e​d + */ + WHITELISTED_ROLE: string + /** + * W​h​i​t​e​l​i​s​t​ ​A​d​m​i​n + */ + WHITELIST_ADMIN_ROLE: string + /** + * P​a​u​s​e​r + */ + PAUSER_ROLE: string + /** + * L​e​v​e​l​ ​O​p​e​r​a​t​o​r + */ + LEVEL_OPERATOR_ROLE: string + /** + * M​a​n​a​g​e​r + */ + MANAGER_ROLE: string + /** + * W​h​i​t​e​l​i​s​t​e​r + */ + WHITELISTER_ROLE: string + /** + * G​r​a​n​t​ ​R​o​l​e + */ + grant_role: string + /** + * R​e​v​o​k​e​ ​R​o​l​e + */ + revoke_role: string + /** + * G​r​a​n​t​i​n​g​.​.​. + */ + granting: string + /** + * R​e​v​o​k​i​n​g​.​.​. + */ + revoking: string + /** + * R​o​l​e​ ​G​r​a​n​t​e​d​ ​S​u​c​c​e​s​s​f​u​l​l​y + */ + grant_success_title: string + /** + * T​h​e​ ​r​o​l​e​ ​h​a​s​ ​b​e​e​n​ ​g​r​a​n​t​e​d​ ​t​o​ ​t​h​e​ ​u​s​e​r​. + */ + grant_success_description: string + /** + * R​o​l​e​ ​R​e​v​o​k​e​d​ ​S​u​c​c​e​s​s​f​u​l​l​y + */ + revoke_success_title: string + /** + * T​h​e​ ​r​o​l​e​ ​h​a​s​ ​b​e​e​n​ ​r​e​v​o​k​e​d​ ​f​r​o​m​ ​t​h​e​ ​u​s​e​r​. + */ + revoke_success_description: string + /** + * R​o​l​e​ ​O​p​e​r​a​t​i​o​n​ ​F​a​i​l​e​d + */ + error_title: string + /** + * T​h​e​r​e​ ​w​a​s​ ​a​n​ ​e​r​r​o​r​ ​w​i​t​h​ ​t​h​e​ ​r​o​l​e​ ​o​p​e​r​a​t​i​o​n​:​ ​{​e​r​r​o​r​} + * @param {string} error + */ + error_description: RequiredParams<'error'> + /** + * P​l​e​a​s​e​ ​e​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​a​d​d​r​e​s​s + */ + invalid_address: string + /** + * P​l​e​a​s​e​ ​s​e​l​e​c​t​ ​a​ ​r​o​l​e + */ + role_required: string + /** + * U​s​e​r​ ​a​d​d​r​e​s​s​ ​i​s​ ​r​e​q​u​i​r​e​d + */ + address_required: string + } + } +} export type TranslationFunctions = { - /** - * Left - */ - left: () => LocalizedString; - /** - * by - */ - by: () => LocalizedString; - /** - * Not published - */ - not_published: () => LocalizedString; - /** - * Homepage - */ - homepage: () => LocalizedString; - /** - * Back - */ - back: () => LocalizedString; - /** - * Start - */ - start: () => LocalizedString; - /** - * End - */ - end: () => LocalizedString; - /** - * Edit - */ - edit: () => LocalizedString; - /** - * On - */ - on: () => LocalizedString; - /** - * All - */ - all: () => LocalizedString; - /** - * Finished - */ - finished: () => LocalizedString; - /** - * Executed - */ - executed: () => LocalizedString; - /** - * Show more - */ - show_more: () => LocalizedString; - /** - * Exit - */ - exit: () => LocalizedString; - /** - * Next - */ - next: () => LocalizedString; - /** - * Learn more - */ - learn_more: () => LocalizedString; - /** - * See details - */ - see_details: () => LocalizedString; - /** - * Select - */ - select: () => LocalizedString; - /** - * Select between - */ - select_between: () => LocalizedString; - /** - * and - */ - and: () => LocalizedString; - /** - * one - */ - one: () => LocalizedString; - /** - * Voting - */ - voting: () => LocalizedString; - /** - * Voters - */ - voters: () => LocalizedString; - /** - * Most voted - */ - most_voted: () => LocalizedString; - /** - * Votes - */ - votes: () => LocalizedString; - /** - * Delete - */ - delete: () => LocalizedString; - /** - * Cancel - */ - cancel: () => LocalizedString; - /** - * % - */ - percentage: () => LocalizedString; - /** - * Submit - */ - submit: () => LocalizedString; - /** - * Submit Vote - */ - submit_vote: () => LocalizedString; - /** - * Voting power - */ - voting_power: () => LocalizedString; - /** - * Your voting power - */ - your_voting_power: () => LocalizedString; - /** - * Voted - */ - voted: () => LocalizedString; - /** - * Vote - */ - vote: () => LocalizedString; - /** - * Wallet - */ - wallet: () => LocalizedString; - /** - * Block # - */ - block: () => LocalizedString; - /** - * Node - */ - node: () => LocalizedString; - /** - * Go back - */ - go_back: () => LocalizedString; - /** - * Optional - */ - optional: () => LocalizedString; - /** - * Buy a Node - */ - buy_a_node: () => LocalizedString; - /** - * {current}/{max} - */ - filed_length: (arg: { current: number; max: number }) => LocalizedString; - /** - * Upload - */ - upload: () => LocalizedString; - /** - * Copied to clipboard - */ - copied_to_clipboard: () => LocalizedString; - /** + /** + * Left + */ + left: () => LocalizedString + /** + * by + */ + by: () => LocalizedString + /** + * Not published + */ + not_published: () => LocalizedString + /** + * Homepage + */ + homepage: () => LocalizedString + /** + * Back + */ + back: () => LocalizedString + /** + * Start + */ + start: () => LocalizedString + /** + * End + */ + end: () => LocalizedString + /** + * Edit + */ + edit: () => LocalizedString + /** + * On + */ + on: () => LocalizedString + /** + * All + */ + all: () => LocalizedString + /** + * Finished + */ + finished: () => LocalizedString + /** + * Executed + */ + executed: () => LocalizedString + /** + * Show more + */ + show_more: () => LocalizedString + /** + * Exit + */ + exit: () => LocalizedString + /** + * Next + */ + next: () => LocalizedString + /** + * Learn more + */ + learn_more: () => LocalizedString + /** + * See details + */ + see_details: () => LocalizedString + /** + * Select + */ + select: () => LocalizedString + /** + * Select between + */ + select_between: () => LocalizedString + /** + * and + */ + and: () => LocalizedString + /** + * one + */ + one: () => LocalizedString + /** + * Voting + */ + voting: () => LocalizedString + /** + * Voters + */ + voters: () => LocalizedString + /** + * Most voted + */ + most_voted: () => LocalizedString + /** + * Votes + */ + votes: () => LocalizedString + /** + * Delete + */ + 'delete': () => LocalizedString + /** + * Cancel + */ + cancel: () => LocalizedString + /** + * % + */ + percentage: () => LocalizedString + /** + * Submit + */ + submit: () => LocalizedString + /** + * Submit Vote + */ + submit_vote: () => LocalizedString + /** + * Voting power + */ + voting_power: () => LocalizedString + /** + * Your voting power + */ + your_voting_power: () => LocalizedString + /** + * Voted + */ + voted: () => LocalizedString + /** + * Vote + */ + vote: () => LocalizedString + /** + * Wallet + */ + wallet: () => LocalizedString + /** + * Block # + */ + block: () => LocalizedString + /** + * Node + */ + node: () => LocalizedString + /** + * Go back + */ + go_back: () => LocalizedString + /** + * Optional + */ + optional: () => LocalizedString + /** + * Buy a Node + */ + buy_a_node: () => LocalizedString + /** + * {current}/{max} + */ + filed_length: (arg: { current: number, max: number }) => LocalizedString + /** + * Upload + */ + upload: () => LocalizedString + /** + * Copied to clipboard + */ + copied_to_clipboard: () => LocalizedString + /** * Image size should be 1280x512px (ratio 3:1). JPG, PNG or SVG of maximum of {size}MB. */ - file_upload_description: (arg: { size: number }) => LocalizedString; - /** - * Select date - */ - select_date: () => LocalizedString; - /** - * Select time - */ - select_time: () => LocalizedString; - /** - * Maximum - */ - maximum: () => LocalizedString; - /** - * Minimum - */ - minimum: () => LocalizedString; - /** - * Option {index} - */ - number_option: (arg: { index: number }) => LocalizedString; - /** - * Continue - */ - continue: () => LocalizedString; - /** - * Results - */ - results: () => LocalizedString; - /** - * Description - */ - description: () => LocalizedString; - /** - * Preview - */ - preview: () => LocalizedString; - /** - * Close - */ - close: () => LocalizedString; - /** - * Confirm - */ - confirm: () => LocalizedString; - /** - * Try Again - */ - try_again: () => LocalizedString; - /** - * Read full description - */ - read_full_description: () => LocalizedString; - /** - * Disconnect - */ - disconnect: () => LocalizedString; - /** - * Connect Wallet - */ - connect_wallet: () => LocalizedString; - /** - * Connect your wallet to vote - */ - connect_wallet_to_vote: () => LocalizedString; - /** - * Comment - */ - comment: () => LocalizedString; - /** - * Add a comment to your vote... - */ - comment_placeholder: () => LocalizedString; - /** - * Migrate - */ - migrate: () => LocalizedString; - /** - * StarGate - */ - stargate: () => LocalizedString; - /** - * How voting power is obtained - */ - learn_how_voting_power: () => LocalizedString; - /** - * Join the discussion on Discourse - */ - discuss_on_discourse: () => LocalizedString; - datepicker: { - /** - * Select date - */ - select_date: () => LocalizedString; - /** - * Previous month - */ - previous_month: () => LocalizedString; - /** - * Next month - */ - next_month: () => LocalizedString; - /** - * Today - */ - today: () => LocalizedString; - weekdays: { - /** - * Mon - */ - mon: () => LocalizedString; - /** - * Tue - */ - tue: () => LocalizedString; - /** - * Wed - */ - wed: () => LocalizedString; - /** - * Thu - */ - thu: () => LocalizedString; - /** - * Fri - */ - fri: () => LocalizedString; - /** - * Sat - */ - sat: () => LocalizedString; - /** - * Sun - */ - sun: () => LocalizedString; - }; - months: { - /** - * January - */ - january: () => LocalizedString; - /** - * February - */ - february: () => LocalizedString; - /** - * March - */ - march: () => LocalizedString; - /** - * April - */ - april: () => LocalizedString; - /** - * May - */ - may: () => LocalizedString; - /** - * June - */ - june: () => LocalizedString; - /** - * July - */ - july: () => LocalizedString; - /** - * August - */ - august: () => LocalizedString; - /** - * September - */ - september: () => LocalizedString; - /** - * October - */ - october: () => LocalizedString; - /** - * November - */ - november: () => LocalizedString; - /** - * December - */ - december: () => LocalizedString; - }; - }; - timepicker: { - /** - * Select time - */ - select_time: () => LocalizedString; - /** - * Select time (UTC) - */ - select_time_24h: () => LocalizedString; - /** - * Hours - */ - hours: () => LocalizedString; - /** - * Minutes - */ - minutes: () => LocalizedString; - /** - * All times are in UTC - */ - utc_notice: () => LocalizedString; - }; - home: { - /** - * Home - */ - title: () => LocalizedString; - /** - * Go to proposals - */ - go_to_proposals: () => LocalizedString; - }; - node_names: { - /** - * not defined - */ - none: () => LocalizedString; - /** - * Strength - */ - strength: () => LocalizedString; - /** - * Thunder - */ - thunder: () => LocalizedString; - /** - * Mjolnir - */ - mjolnir: () => LocalizedString; - /** - * VeThor X - */ - vethorx: () => LocalizedString; - /** - * Strength X - */ - strengthx: () => LocalizedString; - /** - * Thunder X - */ - thunderx: () => LocalizedString; - /** - * Mjolnir X - */ - mjolnirx: () => LocalizedString; - /** - * Flash - */ - flash: () => LocalizedString; - /** - * Lightning - */ - lightning: () => LocalizedString; - /** - * Dawn - */ - dawn: () => LocalizedString; - /** - * Validator - */ - validator: () => LocalizedString; - /** - * Validator (inactive) - */ - inactive_validator: () => LocalizedString; - }; - field_errors: { - /** - * Required - */ - required: () => LocalizedString; - /** - * Invalid format - */ - invalid_format: () => LocalizedString; - /** - * The end date must be after the start date - */ - end_before_start: () => LocalizedString; - /** - * The end date must be in the future - */ - end_before_today: () => LocalizedString; - /** - * The start date must be in the future - */ - start_after_today: () => LocalizedString; - /** - * The end date must be within {days} days of the start date - */ - end_after_max_duration: (arg: { days: string }) => LocalizedString; - /** - * No voters found matching your search criteria. - */ - failed_load_voters: () => LocalizedString; - descriptions_errors: { - /** - * Please replace placeholder text with your own content before submitting the proposal. - */ - placeholders_not_replaced: () => LocalizedString; - /** - * Description cannot be empty. Please provide content for your proposal. - */ - empty_description: () => LocalizedString; - }; - /** - * The Discourse topic does not exist or is not accessible - */ - discourse_topic_not_exist: () => LocalizedString; - }; - voting_list: { - /** - * Voting options: - */ - voting_options: () => LocalizedString; - /** - * Select an option to vote: - */ - option_to_vote: () => LocalizedString; - /** - * Voting has not started yet - */ - voting_has_not_started_yet: () => LocalizedString; - /** - * Please connect your wallet - */ - please_connect_your_wallet: () => LocalizedString; - /** - * You have already voted - */ - you_have_already_voted: () => LocalizedString; - /** - * You don't have enough voting power - */ - you_dont_have_enough_voting_power: () => LocalizedString; - }; - proposal: { - /** - * Proposal - */ - title: () => LocalizedString; - /** - * Proposed by - */ - proposed_by: () => LocalizedString; - /** - * Voting calendar - */ - voting_calendar: () => LocalizedString; - /** - * Confirm in your wallet... - */ - confirm_in_your_wallet: () => LocalizedString; - /** - * Who can vote - */ - who_can_vote: () => LocalizedString; - /** - * VeChain Foundation - */ - vechain_foundation: () => LocalizedString; - /** - * Node holders with voting power will be able to vote on this proposal. - */ - node_holders: () => LocalizedString; - /** - * Voting will start {date} - */ - voting_will_start: (arg: { date: string }) => LocalizedString; - /** - * See your vote details - */ - see_your_vote: () => LocalizedString; - /** - * See all ({voters}) voters - */ - see_all_voters: (arg: { voters: number }) => LocalizedString; - /** - * See first voter - */ - see_first_voter: () => LocalizedString; - /** - * Mark as executed - */ - mark_as_executed: () => LocalizedString; - /** - * Buy another node to increase your voting power on future proposals. - */ - buy_another_node: () => LocalizedString; - /** - * Voting is only possible for Node holders. Buy a node to vote on future proposals or increase your voting power. - */ - buy_a_node: () => LocalizedString; - vote_success: { - /** - * Vote submitted! - */ - title: () => LocalizedString; - /** - * Your vote was submitted successfully. - */ - description: () => LocalizedString; - }; - cancel_proposal: { - /** - * Cancel proposal - */ - title: () => LocalizedString; - /** - * Canceling the proposal means it the voting will not take place and the proposal will not have no results. - */ - description: () => LocalizedString; - /** - * Reason - */ - reason: () => LocalizedString; - /** - * Write the reason for cancellation... - */ - reason_placeholder: () => LocalizedString; - /** - * Proposal canceled successfully - */ - success_title: () => LocalizedString; - /** - * The proposal has been canceled successfully. Voting will not take place. - */ - success_description: () => LocalizedString; - }; - execute_proposal: { - /** - * Mark as Executed - */ - title: () => LocalizedString; - /** - * If the actions of the proposal have already been executed, you can mark this approved proposal as executed for the voters to know. - */ - description: () => LocalizedString; - /** - * Execution / Transaction details - */ - label: () => LocalizedString; - /** - * Insert link with the proof of the execution - */ - link_placeholder: () => LocalizedString; - }; - delete_proposal: { - /** - * Delete proposal - */ - title: () => LocalizedString; - /** - * If you delete this draft, all the information of the proposal will not be possible to recover anymore. - */ - description: () => LocalizedString; - /** - * Are you sure you want to delete it? - */ - confirmation: () => LocalizedString; - /** - * No, go back - */ - no_go_back: () => LocalizedString; - /** - * Yes, Delete - */ - yes_delete: () => LocalizedString; - }; - info_box: { - info: { - /** - * Minimum participation - */ - title: () => LocalizedString; - /** - * A minimum of {quorum}% participation must be reached to validate the voting of the proposal and get approval. - */ - description: (arg: { quorum: number }) => LocalizedString; - }; - approved: { - /** - * Minimum participation reached - */ - title: () => LocalizedString; - /** - * The voting participation reached the minimum required of {quorum}% to get approval. - */ - description: (arg: { quorum: number }) => LocalizedString; - }; - executed: { - /** - * Proposal Approved and Executed - */ - title: () => LocalizedString; - /** - * The voting approved the proposal and the actions have been executed. - */ - description: () => LocalizedString; - }; - "min-not-reached": { - /** - * Minimum participation not reached - */ - title: () => LocalizedString; - /** - * The voting participation didn’t reached the minimum required of {quorum}% to get approval. - */ - description: (arg: { quorum: number }) => LocalizedString; - }; - rejected: { - /** - * Proposal Rejected - */ - title: () => LocalizedString; - /** - * The proposal didn’t get enough votes in favor to get approval. - */ - description: () => LocalizedString; - }; - canceled: { - /** - * Proposal Canceled - */ - title: () => LocalizedString; - /** - * The proposal was canceled by VeChain or the proposer by the following reason: - */ - description: () => LocalizedString; - }; - }; - voting_power: { - /** - * Get more voting power - */ - get_more_voting_power: () => LocalizedString; - /** - * Get voting power - */ - get_voting_power: () => LocalizedString; - /** - * Voting power - */ - title: () => LocalizedString; - /** - * Your voting power was calculated at the time of the snapshot {snapshot}. - */ - calculation: (arg: { snapshot: string }) => LocalizedString; - /** - * Total voting power - */ - total_voting_power: () => LocalizedString; - warnings: { - /** - * The connected wallet has no voting power - */ - zero_voting_power: () => LocalizedString; - /** - * You have legacy nodes that haven’t been migrated yet. Migrate to get more voting power. - */ - legacy_node: () => LocalizedString; - delegated: { - /** - * Your voting power is delegated - */ - title: () => LocalizedString; - /** - * Your voting power is delegated to another node - */ - description: () => LocalizedString; - }; - }; - }; - voters_table: { - filters: { - /** - * Search by address... - */ - search_by_address: () => LocalizedString; - /** - * Voting options - */ - voting_options: () => LocalizedString; - /** - * Node - */ - node: () => LocalizedString; - /** - * Sort by - */ - sort_by: () => LocalizedString; - }; - header: { - /** - * Date - */ - date: () => LocalizedString; - /** - * Address - */ - address: () => LocalizedString; - /** - * Node - */ - node: () => LocalizedString; - /** - * Node ID - */ - node_id: () => LocalizedString; - /** - * Power - */ - voting_power: () => LocalizedString; - /** - * Option - */ - voted_option: () => LocalizedString; - /** - * Transaction ID - */ - transaction_id: () => LocalizedString; - }; - }; - create: { - /** - * Previewing proposal - */ - previewing: () => LocalizedString; - /** - * Create Proposal - */ - title: () => LocalizedString; - /** - * {current} of {total} - */ - steps: (arg: { current: number; total: number }) => LocalizedString; - /** - * Add the main details and setup the calendar - */ - voting_details_desc: () => LocalizedString; - /** - * Define the voting setup details - */ - voting_setup_desc: () => LocalizedString; - /** - * Review all the details before publishing - */ - voting_summary_desc: () => LocalizedString; - /** - * Add new option - */ - add_new_option: () => LocalizedString; - exit_proposal: { - /** - * Exit proposal creation - */ - title: () => LocalizedString; - /** - * By exiting you lose all the information written or you can save this proposal as a draft and finish later? - */ - description: () => LocalizedString; - /** - * You will lose all entered information if you exit now. Saving as a draft is only available on the final step of the form. - */ - description_last_step: () => LocalizedString; - /** - * Be aware that a draft proposal already exists. Saving now will overwrite your previous draft. - */ - description_draft_exist: () => LocalizedString; - /** - * Exit proposal - */ - exit_button: () => LocalizedString; - /** - * Save draft - */ - save_button: () => LocalizedString; - }; - draft_saved: { - /** - * Draft saved! - */ - title: () => LocalizedString; - /** - * The proposal draft has been saved successfully and can now be continued later. - */ - description: () => LocalizedString; - }; - details_form: { - /** - * Title - */ - title: () => LocalizedString; - /** - * What's the proposal title? - */ - title_placeholder: () => LocalizedString; - /** - * Description - */ - description: () => LocalizedString; - /** - * Add a description... - */ - description_placeholder: () => LocalizedString; - /** - * Header image - */ - header_image: () => LocalizedString; - /** - * Discourse url - */ - discourse_url: () => LocalizedString; - /** - * Discourse Topic - */ - discourse_topic: () => LocalizedString; - /** - * your-topic-name - */ - discourse_topic_placeholder: () => LocalizedString; - /** - * Enter the topic name from your VeChain Discourse discussion - */ - discourse_topic_help: () => LocalizedString; - /** - * Voting calendar - */ - voting_calendar: () => LocalizedString; - }; - setup_form: { - /** - * Voting type - */ - voting_type: () => LocalizedString; - /** - * Select the type - */ - voting_type_subtitle: () => LocalizedString; - /** - * Voting Question - */ - voting_question: () => LocalizedString; - /** - * This question should provide exact context to the voting options: - */ - voting_question_subtitle: () => LocalizedString; - /** - * Write the question... - */ - voting_question_placeholder: () => LocalizedString; - /** - * Voting limit - */ - voting_limit: () => LocalizedString; - /** - * Define the minimum and maximum amount of options allowed per voter: - */ - voting_limit_subtitle: () => LocalizedString; - /** - * Voting options - */ - voting_options: () => LocalizedString; - /** - * The “single choice” voting type only allows the voter to select: - */ - voting_choice_subtitle: () => LocalizedString; - /** - * Add between 2 and 30 options to vote: - */ - voting_options_subtitle: () => LocalizedString; - /** - * Add new option - */ - add_new_option: () => LocalizedString; - /** - * Write the voting option... - */ - voting_option_placeholder: () => LocalizedString; - }; - summary_form: { - main_details: { - /** - * Main details - */ - title: () => LocalizedString; - /** - * Calendar - */ - calendar: () => LocalizedString; - }; - voting_setup: { - /** - * Voting setup - */ - title: () => LocalizedString; - /** - * Question - */ - question: () => LocalizedString; - /** - * Type - */ - type: () => LocalizedString; - /** - * Minimum {min} options - Maximum {limit} options - */ - limit: (arg: { limit: number; min: number }) => LocalizedString; - types: { - /** - * Single choice - For / Against / Abstain - */ - SINGLE_CHOICE: () => LocalizedString; - }; - }; - /** - * Publish Proposal - */ - publish_proposal: () => LocalizedString; - /** - * Please note that once the campaign is published, it can't be edited anymore. - */ - publish_description: () => LocalizedString; - /** - * Are you sure you want to publish this proposal? - */ - publish_sub_description: () => LocalizedString; - /** - * Publishing failed - */ - publish_failed: () => LocalizedString; - /** - * The publishing of the proposal couldn’t be completed. Please try again. - */ - publish_failed_description: () => LocalizedString; - /** - * Proposal published - */ - publish_success: () => LocalizedString; - /** - * The proposal has been successfully publish and can now be seen publicly. - */ - publish_success_description: () => LocalizedString; - }; - }; - /** - * Go to StarGate - */ - go_to_stargate: () => LocalizedString; - /** - * Proposal not found - */ - proposal_not_found: () => LocalizedString; - /** - * The proposal you're looking for doesn't exist or may have been removed. It's possible the URL is incorrect or the proposal has been deleted. - */ - proposal_not_found_description: () => LocalizedString; - /** - * Back to Proposals - */ - back_to_proposals: () => LocalizedString; - /** - * Try Again - */ - try_again: () => LocalizedString; - /** - * Starts in - */ - starts_in: () => LocalizedString; - /** - * Ends in - */ - ends_in: () => LocalizedString; - /** - * Timeline - */ - timeline: () => LocalizedString; - /** - * Created - */ - timeline_created: () => LocalizedString; - /** - * Proposal Canceled - */ - proposal_canceled: () => LocalizedString; - /** - * The proposal was canceled by VeChain or the proposer by the following reason: - */ - proposal_canceled_description: () => LocalizedString; - /** - * No reason provided - */ - no_reason_provided: () => LocalizedString; - /** - * Unknown error - */ - unknown_error: () => LocalizedString; - /** - * Failed to execute proposal - */ - failed_to_execute_proposal: () => LocalizedString; - /** - * Proposal Approved and Executed - */ - proposal_approved_and_executed: () => LocalizedString; - /** - * The voting participation reached the minimum quorum to get approval. - */ - proposal_approved: () => LocalizedString; - /** - * The voting approved the proposal and the actions have been executed. - */ - the_voting_approved_the_proposal_and_the_actions_have_been_executed: () => LocalizedString; - /** - * See details - */ - see_details: () => LocalizedString; - /** - * Proposal Rejected - */ - proposal_rejected: () => LocalizedString; - /** - * The proposal didn't get enough votes in favor to get approval. - */ - the_proposal_didnt_get_enough_votes_in_favor_to_get_approval: () => LocalizedString; - /** - * Minimum participant not met - */ - minimum_quorum_not_reached: () => LocalizedString; - /** - * Quorum of {quorum} voting power not reached. - */ - quorum_not_reached: (arg: { quorum: string }) => LocalizedString; - /** - * Quorum of {quorum} voting power not reached yet. - */ - quorum_not_reached_yet: (arg: { quorum: string }) => LocalizedString; - /** - * Quorum reached. - */ - quorum_reached: () => LocalizedString; - /** - * Vote submission failed - */ - vote_submission_failed: () => LocalizedString; - /** - * Vote submitted successfully! - */ - vote_submitted_successfully: () => LocalizedString; - /** - * Submit your vote - */ - submit_your_vote: () => LocalizedString; - /** - * Your vote cannot be changed once submitted. - */ - vote_cannot_be_changed: () => LocalizedString; - /** - * Waiting wallet confirmation... - */ - waiting_wallet_confirmation: () => LocalizedString; - /** - * Confirm vote - */ - confirm_vote: () => LocalizedString; - /** - * You voted - */ - you_voted: () => LocalizedString; - }; - proposals: { - /** - * Proposals - */ - title: () => LocalizedString; - /** - * Create Proposal - */ - create: () => LocalizedString; - /** - * Search proposals... - */ - search_placeholder: () => LocalizedString; - /** - * No proposals found - */ - no_proposals: () => LocalizedString; - /** - * {current} of {total} proposals - */ - pagination: (arg: { current: number; total: number }) => LocalizedString; - }; - statuses: { - /** - * Voted - */ - voted: () => LocalizedString; - }; - badge: { - /** - * Draft - */ - draft: () => LocalizedString; - /** - * Upcoming - */ - upcoming: () => LocalizedString; - /** - * Voting now - */ - voting: () => LocalizedString; - /** - * Approved - */ - approved: () => LocalizedString; - /** - * Executed - */ - executed: () => LocalizedString; - /** - * Canceled - */ - canceled: () => LocalizedString; - /** - * Rejected - */ - rejected: () => LocalizedString; - /** - * Quorum not reached - */ - "min-not-reached": () => LocalizedString; - }; - filters: { - /** - * Filters - */ - title: () => LocalizedString; - /** - * Apply - */ - apply: () => LocalizedString; - /** - * Reset - */ - reset: () => LocalizedString; - /** - * Select all - */ - select_all: () => LocalizedString; - /** - * Deselect all - */ - deselect_all: () => LocalizedString; - sort: { - /** - * Newest - */ - newest: () => LocalizedString; - /** - * Oldest - */ - oldest: () => LocalizedString; - /** - * Most participant - */ - most_participant: () => LocalizedString; - /** - * Least participant - */ - least_participant: () => LocalizedString; - }; - }; - header: { - /** - * VeChainThor Voting Platform - */ - title: () => LocalizedString; - /** - * Vote to shape the future of VeChainThor - */ - description: () => LocalizedString; - /** - * How to vote - */ - how_to_vote: () => LocalizedString; - /** - * Get voting power - */ - how_to_get_voting_power: () => LocalizedString; - }; - stargate_warning: { - /** - * StarGate Node Migration Required - */ - title: () => LocalizedString; - /** - * You have 1 or more non-migrated nodes. Please migrate them as soon as possible to continue voting. - */ - description: () => LocalizedString; - /** - * https://app.stargate.vechain.org/ - */ - migration_link: () => LocalizedString; - /** - * If a proposal has already started, you will not be able to vote on it even after migration. Only future proposals will be available for voting. - */ - ongoing_proposal_warning: () => LocalizedString; - /** - * If you want to continue anyway, write this text: agree-with-this - */ - confirmation_instruction: () => LocalizedString; - /** - * Please type exactly 'agree-with-this' to continue - */ - confirmation_error: () => LocalizedString; - }; - footer: { - /** - * v1.0.0 - */ - version: () => LocalizedString; - /** - * All Rights Reserved © Vechain Foundation San Marino S.r.l. - */ - all_right: () => LocalizedString; - legal: { - /** - * Legal - */ - title: () => LocalizedString; - /** - * Terms of Service - */ - terms_of_service: () => LocalizedString; - /** - * Privacy Policy - */ - privacy_policy: () => LocalizedString; - /** - * Cookies Policy - */ - cookies_policy: () => LocalizedString; - }; - resources: { - /** - * Resources - */ - title: () => LocalizedString; - /** - * Docs - */ - docs: () => LocalizedString; - /** - * StarGate - */ - stargate: () => LocalizedString; - /** - * Support - */ - support: () => LocalizedString; - /** - * Governance Charter - */ - governance_charter: () => LocalizedString; - }; - }; -}; + file_upload_description: (arg: { size: number }) => LocalizedString + /** + * Select date + */ + select_date: () => LocalizedString + /** + * Select time + */ + select_time: () => LocalizedString + /** + * Maximum + */ + maximum: () => LocalizedString + /** + * Minimum + */ + minimum: () => LocalizedString + /** + * Option {index} + */ + number_option: (arg: { index: number }) => LocalizedString + /** + * Continue + */ + 'continue': () => LocalizedString + /** + * Results + */ + results: () => LocalizedString + /** + * Description + */ + description: () => LocalizedString + /** + * Preview + */ + preview: () => LocalizedString + /** + * Close + */ + close: () => LocalizedString + /** + * Confirm + */ + confirm: () => LocalizedString + /** + * Try Again + */ + try_again: () => LocalizedString + /** + * Read full description + */ + read_full_description: () => LocalizedString + /** + * Disconnect + */ + disconnect: () => LocalizedString + /** + * Connect Wallet + */ + connect_wallet: () => LocalizedString + /** + * Connect your wallet to vote + */ + connect_wallet_to_vote: () => LocalizedString + /** + * Comment + */ + comment: () => LocalizedString + /** + * Add a comment to your vote... + */ + comment_placeholder: () => LocalizedString + /** + * Migrate + */ + migrate: () => LocalizedString + /** + * StarGate + */ + stargate: () => LocalizedString + /** + * How voting power is obtained + */ + learn_how_voting_power: () => LocalizedString + /** + * Join the discussion on Discourse + */ + discuss_on_discourse: () => LocalizedString + common: { + time: { + /** + * {count} {count|{1: second, *: seconds}} + */ + seconds: (arg: { count: number | '1' | string }) => LocalizedString + /** + * {count} {count|{1: minute, *: minutes}} + */ + minutes: (arg: { count: number | '1' | string }) => LocalizedString + /** + * {count} {count|{1: hour, *: hours}} + */ + hours: (arg: { count: number | '1' | string }) => LocalizedString + /** + * {count} {count|{1: day, *: days}} + */ + days: (arg: { count: number | '1' | string }) => LocalizedString + } + } + datepicker: { + /** + * Select date + */ + select_date: () => LocalizedString + /** + * Previous month + */ + previous_month: () => LocalizedString + /** + * Next month + */ + next_month: () => LocalizedString + /** + * Today + */ + today: () => LocalizedString + weekdays: { + /** + * Mon + */ + mon: () => LocalizedString + /** + * Tue + */ + tue: () => LocalizedString + /** + * Wed + */ + wed: () => LocalizedString + /** + * Thu + */ + thu: () => LocalizedString + /** + * Fri + */ + fri: () => LocalizedString + /** + * Sat + */ + sat: () => LocalizedString + /** + * Sun + */ + sun: () => LocalizedString + } + months: { + /** + * January + */ + january: () => LocalizedString + /** + * February + */ + february: () => LocalizedString + /** + * March + */ + march: () => LocalizedString + /** + * April + */ + april: () => LocalizedString + /** + * May + */ + may: () => LocalizedString + /** + * June + */ + june: () => LocalizedString + /** + * July + */ + july: () => LocalizedString + /** + * August + */ + august: () => LocalizedString + /** + * September + */ + september: () => LocalizedString + /** + * October + */ + october: () => LocalizedString + /** + * November + */ + november: () => LocalizedString + /** + * December + */ + december: () => LocalizedString + } + } + timepicker: { + /** + * Select time + */ + select_time: () => LocalizedString + /** + * Select time (UTC) + */ + select_time_24h: () => LocalizedString + /** + * Hours + */ + hours: () => LocalizedString + /** + * Minutes + */ + minutes: () => LocalizedString + /** + * All times are in UTC + */ + utc_notice: () => LocalizedString + } + home: { + /** + * Home + */ + title: () => LocalizedString + /** + * Go to proposals + */ + go_to_proposals: () => LocalizedString + } + node_names: { + /** + * not defined + */ + none: () => LocalizedString + /** + * Strength + */ + strength: () => LocalizedString + /** + * Thunder + */ + thunder: () => LocalizedString + /** + * Mjolnir + */ + mjolnir: () => LocalizedString + /** + * VeThor X + */ + vethorx: () => LocalizedString + /** + * Strength X + */ + strengthx: () => LocalizedString + /** + * Thunder X + */ + thunderx: () => LocalizedString + /** + * Mjolnir X + */ + mjolnirx: () => LocalizedString + /** + * Flash + */ + flash: () => LocalizedString + /** + * Lightning + */ + lightning: () => LocalizedString + /** + * Dawn + */ + dawn: () => LocalizedString + /** + * Validator + */ + validator: () => LocalizedString + /** + * Validator (inactive) + */ + inactive_validator: () => LocalizedString + } + field_errors: { + /** + * Required + */ + required: () => LocalizedString + /** + * Invalid format + */ + invalid_format: () => LocalizedString + /** + * Please enter a valid address + */ + invalid_address: () => LocalizedString + /** + * The end date must be after the start date + */ + end_before_start: () => LocalizedString + /** + * The end date must be in the future + */ + end_before_today: () => LocalizedString + /** + * The start date must be in the future + */ + start_after_today: () => LocalizedString + /** + * The end date must be within {days} days of the start date + */ + end_after_max_duration: (arg: { days: string }) => LocalizedString + /** + * No voters found matching your search criteria. + */ + failed_load_voters: () => LocalizedString + descriptions_errors: { + /** + * Please replace placeholder text with your own content before submitting the proposal. + */ + placeholders_not_replaced: () => LocalizedString + /** + * Description cannot be empty. Please provide content for your proposal. + */ + empty_description: () => LocalizedString + } + /** + * The Discourse topic does not exist or is not accessible + */ + discourse_topic_not_exist: () => LocalizedString + } + voting_list: { + /** + * Voting options: + */ + voting_options: () => LocalizedString + /** + * Select an option to vote: + */ + option_to_vote: () => LocalizedString + /** + * Voting has not started yet + */ + voting_has_not_started_yet: () => LocalizedString + /** + * Please connect your wallet + */ + please_connect_your_wallet: () => LocalizedString + /** + * You have already voted + */ + you_have_already_voted: () => LocalizedString + /** + * You don't have enough voting power + */ + you_dont_have_enough_voting_power: () => LocalizedString + } + proposal: { + /** + * Proposal + */ + title: () => LocalizedString + /** + * Proposed by + */ + proposed_by: () => LocalizedString + /** + * Voting calendar + */ + voting_calendar: () => LocalizedString + /** + * Confirm in your wallet... + */ + confirm_in_your_wallet: () => LocalizedString + /** + * Who can vote + */ + who_can_vote: () => LocalizedString + /** + * VeChain Foundation + */ + vechain_foundation: () => LocalizedString + /** + * Node holders with voting power will be able to vote on this proposal. + */ + node_holders: () => LocalizedString + /** + * Voting will start {date} + */ + voting_will_start: (arg: { date: string }) => LocalizedString + /** + * See your vote details + */ + see_your_vote: () => LocalizedString + /** + * See all ({voters}) voters + */ + see_all_voters: (arg: { voters: number }) => LocalizedString + /** + * See first voter + */ + see_first_voter: () => LocalizedString + /** + * Mark as executed + */ + mark_as_executed: () => LocalizedString + /** + * Buy another node to increase your voting power on future proposals. + */ + buy_another_node: () => LocalizedString + /** + * Voting is only possible for Node holders. Buy a node to vote on future proposals or increase your voting power. + */ + buy_a_node: () => LocalizedString + vote_success: { + /** + * Vote submitted! + */ + title: () => LocalizedString + /** + * Your vote was submitted successfully. + */ + description: () => LocalizedString + } + cancel_proposal: { + /** + * Cancel proposal + */ + title: () => LocalizedString + /** + * Canceling the proposal means it the voting will not take place and the proposal will not have no results. + */ + description: () => LocalizedString + /** + * Reason + */ + reason: () => LocalizedString + /** + * Write the reason for cancellation... + */ + reason_placeholder: () => LocalizedString + /** + * Proposal canceled successfully + */ + success_title: () => LocalizedString + /** + * The proposal has been canceled successfully. Voting will not take place. + */ + success_description: () => LocalizedString + } + execute_proposal: { + /** + * Mark as Executed + */ + title: () => LocalizedString + /** + * If the actions of the proposal have already been executed, you can mark this approved proposal as executed for the voters to know. + */ + description: () => LocalizedString + /** + * Execution / Transaction details + */ + label: () => LocalizedString + /** + * Insert link with the proof of the execution + */ + link_placeholder: () => LocalizedString + } + delete_proposal: { + /** + * Delete proposal + */ + title: () => LocalizedString + /** + * If you delete this draft, all the information of the proposal will not be possible to recover anymore. + */ + description: () => LocalizedString + /** + * Are you sure you want to delete it? + */ + confirmation: () => LocalizedString + /** + * No, go back + */ + no_go_back: () => LocalizedString + /** + * Yes, Delete + */ + yes_delete: () => LocalizedString + } + info_box: { + info: { + /** + * Minimum participation + */ + title: () => LocalizedString + /** + * A minimum of {quorum}% participation must be reached to validate the voting of the proposal and get approval. + */ + description: (arg: { quorum: number }) => LocalizedString + } + approved: { + /** + * Minimum participation reached + */ + title: () => LocalizedString + /** + * The voting participation reached the minimum required of {quorum}% to get approval. + */ + description: (arg: { quorum: number }) => LocalizedString + } + executed: { + /** + * Proposal Approved and Executed + */ + title: () => LocalizedString + /** + * The voting approved the proposal and the actions have been executed. + */ + description: () => LocalizedString + } + 'min-not-reached': { + /** + * Minimum participation not reached + */ + title: () => LocalizedString + /** + * The voting participation didn’t reached the minimum required of {quorum}% to get approval. + */ + description: (arg: { quorum: number }) => LocalizedString + } + rejected: { + /** + * Proposal Rejected + */ + title: () => LocalizedString + /** + * The proposal didn’t get enough votes in favor to get approval. + */ + description: () => LocalizedString + } + canceled: { + /** + * Proposal Canceled + */ + title: () => LocalizedString + /** + * The proposal was canceled by VeChain or the proposer by the following reason: + */ + description: () => LocalizedString + } + } + voting_power: { + /** + * Get more voting power + */ + get_more_voting_power: () => LocalizedString + /** + * Get voting power + */ + get_voting_power: () => LocalizedString + /** + * Voting power + */ + title: () => LocalizedString + /** + * Your voting power was calculated at the time of the snapshot {snapshot}. + */ + calculation: (arg: { snapshot: string }) => LocalizedString + /** + * Total voting power + */ + total_voting_power: () => LocalizedString + warnings: { + /** + * The connected wallet has no voting power + */ + zero_voting_power: () => LocalizedString + /** + * You have legacy nodes that haven’t been migrated yet. Migrate to get more voting power. + */ + legacy_node: () => LocalizedString + delegated: { + /** + * Your voting power is delegated + */ + title: () => LocalizedString + /** + * Your voting power is delegated to another node + */ + description: () => LocalizedString + } + } + } + voters_table: { + filters: { + /** + * Search by address... + */ + search_by_address: () => LocalizedString + /** + * Voting options + */ + voting_options: () => LocalizedString + /** + * Node + */ + node: () => LocalizedString + /** + * Sort by + */ + sort_by: () => LocalizedString + } + header: { + /** + * Date + */ + date: () => LocalizedString + /** + * Address + */ + address: () => LocalizedString + /** + * Node + */ + node: () => LocalizedString + /** + * Node ID + */ + node_id: () => LocalizedString + /** + * Power + */ + voting_power: () => LocalizedString + /** + * Option + */ + voted_option: () => LocalizedString + /** + * Transaction ID + */ + transaction_id: () => LocalizedString + } + } + create: { + /** + * Previewing proposal + */ + previewing: () => LocalizedString + /** + * Create Proposal + */ + title: () => LocalizedString + /** + * {current} of {total} + */ + steps: (arg: { current: number, total: number }) => LocalizedString + /** + * Add the main details and setup the calendar + */ + voting_details_desc: () => LocalizedString + /** + * Define the voting setup details + */ + voting_setup_desc: () => LocalizedString + /** + * Review all the details before publishing + */ + voting_summary_desc: () => LocalizedString + /** + * Add new option + */ + add_new_option: () => LocalizedString + exit_proposal: { + /** + * Exit proposal creation + */ + title: () => LocalizedString + /** + * By exiting you lose all the information written or you can save this proposal as a draft and finish later? + */ + description: () => LocalizedString + /** + * You will lose all entered information if you exit now. Saving as a draft is only available on the final step of the form. + */ + description_last_step: () => LocalizedString + /** + * Be aware that a draft proposal already exists. Saving now will overwrite your previous draft. + */ + description_draft_exist: () => LocalizedString + /** + * Exit proposal + */ + exit_button: () => LocalizedString + /** + * Save draft + */ + save_button: () => LocalizedString + } + draft_saved: { + /** + * Draft saved! + */ + title: () => LocalizedString + /** + * The proposal draft has been saved successfully and can now be continued later. + */ + description: () => LocalizedString + } + details_form: { + /** + * Title + */ + title: () => LocalizedString + /** + * What's the proposal title? + */ + title_placeholder: () => LocalizedString + /** + * Description + */ + description: () => LocalizedString + /** + * Add a description... + */ + description_placeholder: () => LocalizedString + /** + * Header image + */ + header_image: () => LocalizedString + /** + * Discourse url + */ + discourse_url: () => LocalizedString + /** + * Discourse Topic + */ + discourse_topic: () => LocalizedString + /** + * your-topic-name + */ + discourse_topic_placeholder: () => LocalizedString + /** + * Enter the topic name from your VeChain Discourse discussion + */ + discourse_topic_help: () => LocalizedString + /** + * Voting calendar + */ + voting_calendar: () => LocalizedString + } + setup_form: { + /** + * Voting type + */ + voting_type: () => LocalizedString + /** + * Select the type + */ + voting_type_subtitle: () => LocalizedString + /** + * Voting Question + */ + voting_question: () => LocalizedString + /** + * This question should provide exact context to the voting options: + */ + voting_question_subtitle: () => LocalizedString + /** + * Write the question... + */ + voting_question_placeholder: () => LocalizedString + /** + * Voting limit + */ + voting_limit: () => LocalizedString + /** + * Define the minimum and maximum amount of options allowed per voter: + */ + voting_limit_subtitle: () => LocalizedString + /** + * Voting options + */ + voting_options: () => LocalizedString + /** + * The “single choice” voting type only allows the voter to select: + */ + voting_choice_subtitle: () => LocalizedString + /** + * Add between 2 and 30 options to vote: + */ + voting_options_subtitle: () => LocalizedString + /** + * Add new option + */ + add_new_option: () => LocalizedString + /** + * Write the voting option... + */ + voting_option_placeholder: () => LocalizedString + } + summary_form: { + main_details: { + /** + * Main details + */ + title: () => LocalizedString + /** + * Calendar + */ + calendar: () => LocalizedString + } + voting_setup: { + /** + * Voting setup + */ + title: () => LocalizedString + /** + * Question + */ + question: () => LocalizedString + /** + * Type + */ + type: () => LocalizedString + /** + * Minimum {min} options - Maximum {limit} options + */ + limit: (arg: { limit: number, min: number }) => LocalizedString + types: { + /** + * Single choice - For / Against / Abstain + */ + SINGLE_CHOICE: () => LocalizedString + } + } + /** + * Publish Proposal + */ + publish_proposal: () => LocalizedString + /** + * Please note that once the campaign is published, it can't be edited anymore. + */ + publish_description: () => LocalizedString + /** + * Are you sure you want to publish this proposal? + */ + publish_sub_description: () => LocalizedString + /** + * Publishing failed + */ + publish_failed: () => LocalizedString + /** + * The publishing of the proposal couldn’t be completed. Please try again. + */ + publish_failed_description: () => LocalizedString + /** + * Proposal published + */ + publish_success: () => LocalizedString + /** + * The proposal has been successfully publish and can now be seen publicly. + */ + publish_success_description: () => LocalizedString + } + } + /** + * Go to StarGate + */ + go_to_stargate: () => LocalizedString + /** + * Proposal not found + */ + proposal_not_found: () => LocalizedString + /** + * The proposal you're looking for doesn't exist or may have been removed. It's possible the URL is incorrect or the proposal has been deleted. + */ + proposal_not_found_description: () => LocalizedString + /** + * Back to Proposals + */ + back_to_proposals: () => LocalizedString + /** + * Try Again + */ + try_again: () => LocalizedString + /** + * Starts in + */ + starts_in: () => LocalizedString + /** + * Ends in + */ + ends_in: () => LocalizedString + /** + * Timeline + */ + timeline: () => LocalizedString + /** + * Created + */ + timeline_created: () => LocalizedString + /** + * Proposal Canceled + */ + proposal_canceled: () => LocalizedString + /** + * The proposal was canceled by VeChain or the proposer by the following reason: + */ + proposal_canceled_description: () => LocalizedString + /** + * No reason provided + */ + no_reason_provided: () => LocalizedString + /** + * Unknown error + */ + unknown_error: () => LocalizedString + /** + * Failed to execute proposal + */ + failed_to_execute_proposal: () => LocalizedString + /** + * Proposal Approved and Executed + */ + proposal_approved_and_executed: () => LocalizedString + /** + * The voting participation reached the minimum quorum to get approval. + */ + proposal_approved: () => LocalizedString + /** + * The voting approved the proposal and the actions have been executed. + */ + the_voting_approved_the_proposal_and_the_actions_have_been_executed: () => LocalizedString + /** + * See details + */ + see_details: () => LocalizedString + /** + * Proposal Rejected + */ + proposal_rejected: () => LocalizedString + /** + * The proposal didn't get enough votes in favor to get approval. + */ + the_proposal_didnt_get_enough_votes_in_favor_to_get_approval: () => LocalizedString + /** + * Minimum participant not met + */ + minimum_quorum_not_reached: () => LocalizedString + /** + * Quorum of {quorum} voting power not reached. + */ + quorum_not_reached: (arg: { quorum: string }) => LocalizedString + /** + * Quorum of {quorum} voting power not reached yet. + */ + quorum_not_reached_yet: (arg: { quorum: string }) => LocalizedString + /** + * Quorum reached. + */ + quorum_reached: () => LocalizedString + /** + * Vote submission failed + */ + vote_submission_failed: () => LocalizedString + /** + * Vote submitted successfully! + */ + vote_submitted_successfully: () => LocalizedString + /** + * Submit your vote + */ + submit_your_vote: () => LocalizedString + /** + * Your vote cannot be changed once submitted. + */ + vote_cannot_be_changed: () => LocalizedString + /** + * Waiting wallet confirmation... + */ + waiting_wallet_confirmation: () => LocalizedString + /** + * Confirm vote + */ + confirm_vote: () => LocalizedString + /** + * You voted + */ + you_voted: () => LocalizedString + } + proposals: { + /** + * Proposals + */ + title: () => LocalizedString + /** + * Create Proposal + */ + create: () => LocalizedString + /** + * Search proposals... + */ + search_placeholder: () => LocalizedString + /** + * No proposals found + */ + no_proposals: () => LocalizedString + /** + * {current} of {total} proposals + */ + pagination: (arg: { current: number, total: number }) => LocalizedString + } + statuses: { + /** + * Voted + */ + voted: () => LocalizedString + } + badge: { + /** + * Draft + */ + draft: () => LocalizedString + /** + * Upcoming + */ + upcoming: () => LocalizedString + /** + * Voting now + */ + voting: () => LocalizedString + /** + * Approved + */ + approved: () => LocalizedString + /** + * Executed + */ + executed: () => LocalizedString + /** + * Canceled + */ + canceled: () => LocalizedString + /** + * Rejected + */ + rejected: () => LocalizedString + /** + * Quorum not reached + */ + 'min-not-reached': () => LocalizedString + } + filters: { + /** + * Filters + */ + title: () => LocalizedString + /** + * Apply + */ + apply: () => LocalizedString + /** + * Reset + */ + reset: () => LocalizedString + /** + * Select all + */ + select_all: () => LocalizedString + /** + * Deselect all + */ + deselect_all: () => LocalizedString + sort: { + /** + * Newest + */ + newest: () => LocalizedString + /** + * Oldest + */ + oldest: () => LocalizedString + /** + * Most participant + */ + most_participant: () => LocalizedString + /** + * Least participant + */ + least_participant: () => LocalizedString + } + } + header: { + /** + * VeChainThor Voting Platform + */ + title: () => LocalizedString + /** + * Vote to shape the future of VeChainThor + */ + description: () => LocalizedString + /** + * How to vote + */ + how_to_vote: () => LocalizedString + /** + * Get voting power + */ + how_to_get_voting_power: () => LocalizedString + } + stargate_warning: { + /** + * StarGate Node Migration Required + */ + title: () => LocalizedString + /** + * You have 1 or more non-migrated nodes. Please migrate them as soon as possible to continue voting. + */ + description: () => LocalizedString + /** + * https://app.stargate.vechain.org/ + */ + migration_link: () => LocalizedString + /** + * If a proposal has already started, you will not be able to vote on it even after migration. Only future proposals will be available for voting. + */ + ongoing_proposal_warning: () => LocalizedString + /** + * If you want to continue anyway, write this text: agree-with-this + */ + confirmation_instruction: () => LocalizedString + /** + * Please type exactly 'agree-with-this' to continue + */ + confirmation_error: () => LocalizedString + } + footer: { + /** + * v1.0.0 + */ + version: () => LocalizedString + /** + * All Rights Reserved © Vechain Foundation San Marino S.r.l. + */ + all_right: () => LocalizedString + legal: { + /** + * Legal + */ + title: () => LocalizedString + /** + * Terms of Service + */ + terms_of_service: () => LocalizedString + /** + * Privacy Policy + */ + privacy_policy: () => LocalizedString + /** + * Cookies Policy + */ + cookies_policy: () => LocalizedString + } + resources: { + /** + * Resources + */ + title: () => LocalizedString + /** + * Docs + */ + docs: () => LocalizedString + /** + * StarGate + */ + stargate: () => LocalizedString + /** + * Support + */ + support: () => LocalizedString + /** + * Governance Charter + */ + governance_charter: () => LocalizedString + } + } + admin: { + /** + * Admin Dashboard + */ + title: () => LocalizedString + tabs: { + /** + * Contracts + */ + contracts: () => LocalizedString + /** + * Utils + */ + utils: () => LocalizedString + /** + * Users + */ + users: () => LocalizedString + /** + * Governance Settings + */ + governance_settings: () => LocalizedString + /** + * Voting Power Query + */ + voting_power_timepoint: () => LocalizedString + } + contracts: { + /** + * VeVote + */ + vevote: () => LocalizedString + /** + * Node Management + */ + node_management: () => LocalizedString + /** + * Stargate Nodes + */ + stargate_nodes: () => LocalizedString + } + vevote_contract: { + /** + * Contract Address: + */ + contract_address: () => LocalizedString + /** + * VeVote Contract Information + */ + title: () => LocalizedString + /** + * Loading VeVote Contract Information... + */ + loading: () => LocalizedString + /** + * Error loading VeVote contract data: {error} + */ + error: (arg: { error: string }) => LocalizedString + /** + * No VeVote contract data available + */ + no_data: () => LocalizedString + /** + * Contract Version + */ + contract_version: () => LocalizedString + /** + * Quorum Numerator + */ + quorum_numerator: () => LocalizedString + /** + * Quorum Denominator + */ + quorum_denominator: () => LocalizedString + /** + * Min Voting Delay + */ + min_voting_delay: () => LocalizedString + /** + * Min Voting Duration + */ + min_voting_duration: () => LocalizedString + /** + * Max Voting Duration + */ + max_voting_duration: () => LocalizedString + /** + * Min Staked Amount + */ + min_staked_amount: () => LocalizedString + /** + * {percentage}% required + */ + quorum_percentage: (arg: { percentage: number }) => LocalizedString + /** + * Contract Information + */ + contract_info_title: () => LocalizedString + } + node_management: { + /** + * Node Management Contract Information + */ + title: () => LocalizedString + /** + * Enter a wallet address to view detailed node ownership and delegation information for that account. + */ + help_text: () => LocalizedString + /** + * User Address + */ + user_address_label: () => LocalizedString + /** + * Enter user address (0x...) + */ + user_address_placeholder: () => LocalizedString + /** + * Load User Node Info + */ + load_button: () => LocalizedString + /** + * Loading... + */ + loading_button: () => LocalizedString + /** + * Loading user node information... + */ + loading_text: () => LocalizedString + /** + * Node Information for {address} + */ + node_info_title: (arg: { address: string }) => LocalizedString + /** + * Is Node Holder + */ + is_node_holder: () => LocalizedString + /** + * Is Node Delegator + */ + is_node_delegator: () => LocalizedString + /** + * Owned Nodes + */ + owned_nodes: () => LocalizedString + /** + * Managed Nodes + */ + managed_nodes: () => LocalizedString + /** + * IDs: {ids} + */ + ids_label: (arg: { ids: string }) => LocalizedString + /** + * Yes + */ + yes: () => LocalizedString + /** + * No + */ + no: () => LocalizedString + /** + * Error loading node data: {error} + */ + error: (arg: { error: string }) => LocalizedString + /** + * Node Information + */ + card_title: () => LocalizedString + /** + * Results for {address} + */ + results_for: (arg: { address: string }) => LocalizedString + /** + * No node information available for this address + */ + no_results: () => LocalizedString + /** + * Available Methods + */ + methods_title: () => LocalizedString + /** + * This component demonstrates the NodeManagementService functionality. You can extend it to show additional statistics like total nodes, delegation stats, etc. + */ + methods_description: () => LocalizedString + } + stargate_nodes: { + /** + * Stargate NFT Contract Information + */ + title: () => LocalizedString + /** + * Loading Stargate NFT Information... + */ + loading: () => LocalizedString + /** + * Error loading Stargate NFT data: {error} + */ + error: (arg: { error: string }) => LocalizedString + /** + * No Stargate NFT data available + */ + no_data: () => LocalizedString + /** + * Total Supply + */ + total_supply: () => LocalizedString + /** + * Available Levels + */ + available_levels: () => LocalizedString + /** + * Level IDs: {ids} + */ + level_ids: (arg: { ids: string }) => LocalizedString + /** + * Level Details + */ + level_details_title: () => LocalizedString + table: { + /** + * Level + */ + level: () => LocalizedString + /** + * Name + */ + name: () => LocalizedString + /** + * Is X-Node + */ + is_x_node: () => LocalizedString + /** + * Maturity Blocks + */ + maturity_blocks: () => LocalizedString + /** + * VET Required + */ + vet_required: () => LocalizedString + /** + * Circulating + */ + circulating: () => LocalizedString + /** + * Cap + */ + cap: () => LocalizedString + } + /** + * Yes + */ + yes: () => LocalizedString + /** + * No + */ + no: () => LocalizedString + /** + * N/A + */ + not_available: () => LocalizedString + /** + * Contract Information + */ + contract_info_title: () => LocalizedString + /** + * This displays comprehensive information about the Stargate NFT contract including level configurations, supply information, and staking requirements. + */ + contract_description: () => LocalizedString + } + /** + * {number}s + */ + format_seconds: (arg: { number: number }) => LocalizedString + /** + * {minutes} min ({seconds}s) + */ + format_minutes_seconds: (arg: { minutes: number, seconds: number }) => LocalizedString + /** + * {number} days + */ + format_days: (arg: { number: number }) => LocalizedString + /** + * {amount} VET + */ + vet_format: (arg: { amount: string }) => LocalizedString + governance_settings: { + /** + * Governance Settings + */ + title: () => LocalizedString + /** + * Configure VeVote governance parameters + */ + description: () => LocalizedString + /** + * Quorum Numerator + */ + quorum_numerator_label: () => LocalizedString + /** + * Required votes numerator (current: {current}) + */ + quorum_numerator_help: (arg: { current: number }) => LocalizedString + /** + * Min Voting Delay + */ + min_voting_delay_label: () => LocalizedString + /** + * Minimum delay before voting starts (in blocks) + */ + min_voting_delay_help: () => LocalizedString + /** + * Min Voting Duration + */ + min_voting_duration_label: () => LocalizedString + /** + * Minimum voting duration (in blocks) + */ + min_voting_duration_help: () => LocalizedString + /** + * Max Voting Duration + */ + max_voting_duration_label: () => LocalizedString + /** + * Maximum voting duration (in blocks) + */ + max_voting_duration_help: () => LocalizedString + /** + * Min Staked VET Amount + */ + min_staked_vet_amount_label: () => LocalizedString + /** + * Minimum VET amount required to stake + */ + min_staked_vet_amount_help: () => LocalizedString + /** + * Level ID Multipliers + */ + level_multipliers_label: () => LocalizedString + /** + * Voting weight multipliers for each level ID (scaled by 100) + */ + level_multipliers_help: () => LocalizedString + /** + * Validator + */ + validator_multiplier: () => LocalizedString + /** + * Strength + */ + strength: () => LocalizedString + /** + * Thunder + */ + thunder: () => LocalizedString + /** + * Mjolnir + */ + mjolnir: () => LocalizedString + /** + * VeThor X + */ + vethor_x: () => LocalizedString + /** + * Strength X + */ + strength_x: () => LocalizedString + /** + * Thunder X + */ + thunder_x: () => LocalizedString + /** + * Mjolnir X + */ + mjolnir_x: () => LocalizedString + /** + * Dawn Node + */ + dawn: () => LocalizedString + /** + * Lightning Node + */ + lightning: () => LocalizedString + /** + * Flash Node + */ + flash: () => LocalizedString + /** + * Level ID + */ + level_id: () => LocalizedString + /** + * Node Name + */ + node_name: () => LocalizedString + /** + * Current + */ + current_multiplier: () => LocalizedString + /** + * New Value + */ + new_multiplier: () => LocalizedString + /** + * Update Settings + */ + update_settings: () => LocalizedString + /** + * Update Governance Settings + */ + update_governance_settings: () => LocalizedString + /** + * Update Multipliers + */ + update_multipliers: () => LocalizedString + /** + * Updating... + */ + updating: () => LocalizedString + /** + * Settings Updated Successfully + */ + success_title: () => LocalizedString + /** + * Governance settings have been updated and are now active. + */ + success_description: () => LocalizedString + /** + * Failed to Update Settings + */ + error_title: () => LocalizedString + /** + * There was an error updating the governance settings: {error} + */ + error_description: (arg: { error: string }) => LocalizedString + /** + * Value must be between {min} and {max} + */ + invalid_range: (arg: { max: number, min: number }) => LocalizedString + /** + * This field is required + */ + required_field: () => LocalizedString + /** + * Current: {value} + */ + current_value: (arg: { value: string }) => LocalizedString + } + user_management: { + /** + * User Management + */ + title: () => LocalizedString + /** + * Grant or revoke roles for VeVote governance + */ + description: () => LocalizedString + /** + * User Address + */ + user_address_label: () => LocalizedString + /** + * Enter user address (0x...) + */ + user_address_placeholder: () => LocalizedString + /** + * Role + */ + role_label: () => LocalizedString + /** + * Select a role + */ + role_placeholder: () => LocalizedString + /** + * Current Roles: + */ + current_roles_label: () => LocalizedString + /** + * Checking roles... + */ + checking_roles: () => LocalizedString + /** + * No roles assigned + */ + no_roles_assigned: () => LocalizedString + } + role_users: { + /** + * Role Users + */ + title: () => LocalizedString + /** + * Select a role to view all users who have been granted that specific role. + */ + help_text: () => LocalizedString + /** + * Role + */ + role_label: () => LocalizedString + /** + * Select a role to query + */ + role_placeholder: () => LocalizedString + /** + * Query Role Users + */ + query_button: () => LocalizedString + /** + * Loading... + */ + loading: () => LocalizedString + /** + * Fetching users with selected role... + */ + loading_text: () => LocalizedString + /** + * Users with Role + */ + results_title: () => LocalizedString + /** + * {count} {count|{1: user, *: users}} + */ + user_count: (arg: { count: number | '1' | string }) => LocalizedString + /** + * Role: {role} + */ + role_selected: (arg: { role: string }) => LocalizedString + /** + * Granted: {date} + */ + granted_at: (arg: { date: string }) => LocalizedString + /** + * View TX + */ + view_tx: () => LocalizedString + /** + * No users found with this role + */ + no_users: () => LocalizedString + /** + * Scroll to see more users + */ + scrollable_hint: () => LocalizedString + /** + * Error fetching role users: {error} + */ + error_description: (arg: { error: string }) => LocalizedString + } + user_role_checker: { + /** + * Your Permissions + */ + title: () => LocalizedString + /** + * Connect wallet to see your roles + */ + connect_wallet_message: () => LocalizedString + /** + * Checking your roles... + */ + checking_roles: () => LocalizedString + } + voting_power_timepoint: { + /** + * Address: + */ + address: () => LocalizedString + /** + * Timepoint: + */ + timepoint: () => LocalizedString + /** + * Master Address: + */ + master_address: () => LocalizedString + /** + * Voting Power at Timepoint + */ + title: () => LocalizedString + /** + * Query historical voting power for any wallet at a specific timepoint + */ + description: () => LocalizedString + /** + * Wallet Address + */ + address_label: () => LocalizedString + /** + * Enter wallet address (0x...) + */ + address_placeholder: () => LocalizedString + /** + * Timepoint + */ + timepoint_label: () => LocalizedString + /** + * Enter timepoint + */ + timepoint_placeholder: () => LocalizedString + /** + * Master Address + */ + master_address_label: () => LocalizedString + /** + * Enter master address for validator power (0x...) + */ + master_address_placeholder: () => LocalizedString + /** + * Query Voting Power + */ + query_button: () => LocalizedString + /** + * Querying... + */ + querying: () => LocalizedString + /** + * Voting Power Results + */ + results_title: () => LocalizedString + /** + * Node-based Power: + */ + node_power_label: () => LocalizedString + /** + * Validator Power: + */ + validator_power_label: () => LocalizedString + /** + * Total Power: + */ + total_power_label: () => LocalizedString + /** + * No results to display + */ + no_results: () => LocalizedString + /** + * Please enter a valid address + */ + invalid_address: () => LocalizedString + /** + * Please enter a valid timepoint/block number + */ + invalid_timepoint: () => LocalizedString + /** + * Wallet address is required + */ + address_required: () => LocalizedString + /** + * Timepoint is required + */ + timepoint_required: () => LocalizedString + /** + * Query Failed + */ + error_title: () => LocalizedString + /** + * There was an error querying voting power: {error} + */ + error_description: (arg: { error: string }) => LocalizedString + /** + * Enter a wallet address and timepoint to view historical voting power. Optionally provide a master address to check validator-delegated power. + */ + help_text: () => LocalizedString + } + /** + * Unknown error + */ + unknown_error: () => LocalizedString + /** + * This operation is sensitive. Please use with caution. + */ + sensitive_operation_warning: () => LocalizedString + /** + * Your Permissions + */ + your_permissions: () => LocalizedString + common_roles: { + /** + * Default Admin + */ + DEFAULT_ADMIN_ROLE: () => LocalizedString + /** + * Executor + */ + EXECUTOR_ROLE: () => LocalizedString + /** + * Settings Manager + */ + SETTINGS_MANAGER_ROLE: () => LocalizedString + /** + * Node Weight Manager + */ + NODE_WEIGHT_MANAGER_ROLE: () => LocalizedString + /** + * Upgrader + */ + UPGRADER_ROLE: () => LocalizedString + /** + * Whitelisted + */ + WHITELISTED_ROLE: () => LocalizedString + /** + * Whitelist Admin + */ + WHITELIST_ADMIN_ROLE: () => LocalizedString + /** + * Pauser + */ + PAUSER_ROLE: () => LocalizedString + /** + * Level Operator + */ + LEVEL_OPERATOR_ROLE: () => LocalizedString + /** + * Manager + */ + MANAGER_ROLE: () => LocalizedString + /** + * Whitelister + */ + WHITELISTER_ROLE: () => LocalizedString + /** + * Grant Role + */ + grant_role: () => LocalizedString + /** + * Revoke Role + */ + revoke_role: () => LocalizedString + /** + * Granting... + */ + granting: () => LocalizedString + /** + * Revoking... + */ + revoking: () => LocalizedString + /** + * Role Granted Successfully + */ + grant_success_title: () => LocalizedString + /** + * The role has been granted to the user. + */ + grant_success_description: () => LocalizedString + /** + * Role Revoked Successfully + */ + revoke_success_title: () => LocalizedString + /** + * The role has been revoked from the user. + */ + revoke_success_description: () => LocalizedString + /** + * Role Operation Failed + */ + error_title: () => LocalizedString + /** + * There was an error with the role operation: {error} + */ + error_description: (arg: { error: string }) => LocalizedString + /** + * Please enter a valid address + */ + invalid_address: () => LocalizedString + /** + * Please select a role + */ + role_required: () => LocalizedString + /** + * User address is required + */ + address_required: () => LocalizedString + } + } +} -export type Formatters = {}; +export type Formatters = {} diff --git a/apps/frontend/src/icons/Menu.tsx b/apps/frontend/src/icons/Menu.tsx new file mode 100644 index 00000000..5abd676c --- /dev/null +++ b/apps/frontend/src/icons/Menu.tsx @@ -0,0 +1,20 @@ +import { SVGProps } from "react"; + +export const Menu = (props: SVGProps) => { + return ( + + + + + + ); +}; \ No newline at end of file diff --git a/apps/frontend/src/icons/index.ts b/apps/frontend/src/icons/index.ts index 14112e7c..0110e14a 100644 --- a/apps/frontend/src/icons/index.ts +++ b/apps/frontend/src/icons/index.ts @@ -49,6 +49,7 @@ import { Logout } from "./Logout"; import { Node } from "./Node"; import { MessageSquare } from "./MessageSquare"; import { Discourse } from "./Discourse"; +import { Menu } from "./Menu"; export * from "./ThumbsUpIcon"; export * from "./ThumbsDownIcon"; export * from "./AbstainIcon"; @@ -105,4 +106,5 @@ export { Logout as LogoutIcon, Node as NodeIcon, Discourse as DiscourseIcon, + Menu as MenuIcon, }; diff --git a/apps/frontend/src/pages/Admin/AdminDashboard.tsx b/apps/frontend/src/pages/Admin/AdminDashboard.tsx new file mode 100644 index 00000000..7308ef82 --- /dev/null +++ b/apps/frontend/src/pages/Admin/AdminDashboard.tsx @@ -0,0 +1,29 @@ +import { useBreakpointValue } from "@chakra-ui/react"; +import { VeVoteContract } from "./components/Contracts/VeVoteContract"; +import { NodeManagement } from "./components/Contracts/NodeManagement"; +import { StargateNodes } from "./components/Contracts/StargateNodes"; +import { UserManagement } from "./components/Utils/UserManagement"; +import { GovernanceSettings } from "./components/Utils/GovernanceSettings"; +import { ResponsiveNavigation } from "./components/navigation/ResponsiveNavigation"; +import { PageContainer } from "@/components/PageContainer"; + +export function AdminDashboard() { + const containerPadding = useBreakpointValue({ + base: 4, + md: 8, + }); + + const contractsContent = [ + , + , + , + ]; + + const utilsContent = [, ]; + + return ( + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Contracts/NodeManagement.tsx b/apps/frontend/src/pages/Admin/components/Contracts/NodeManagement.tsx new file mode 100644 index 00000000..dd25933b --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Contracts/NodeManagement.tsx @@ -0,0 +1,54 @@ +import { Box, Heading, Text, HStack, VStack, useBreakpointValue } from "@chakra-ui/react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { CopyLink } from "@/components/ui/CopyLink"; +import { getConfig } from "@repo/config"; +import { formatAddress } from "@/utils/address"; +import { NodeManagementContractInfo } from "./components/NodeManagementContractInfo"; +import { useMemo } from "react"; +import { useContractAddresses } from "../../hooks/useContractAddresses"; + +const EXPLORER_URL = getConfig(import.meta.env.VITE_APP_ENV).network.explorerUrl; + +export function NodeManagement() { + const { LL } = useI18nContext(); + + const { contractAddresses } = useContractAddresses(); + + const nodeManagementContractAddress = useMemo( + () => contractAddresses?.nodeManagementAddress, + [contractAddresses?.nodeManagementAddress], + ); + const stackSpacing = useBreakpointValue({ + base: 4, + md: 6, + }); + + return ( + + + + + {LL.admin.node_management.title()} + + {nodeManagementContractAddress && ( + + {LL.admin.vevote_contract.contract_address()} + + {formatAddress(nodeManagementContractAddress)} + + + )} + + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Contracts/StargateNodes.tsx b/apps/frontend/src/pages/Admin/components/Contracts/StargateNodes.tsx new file mode 100644 index 00000000..ac518d27 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Contracts/StargateNodes.tsx @@ -0,0 +1,77 @@ +import { CopyLink } from "@/components/ui/CopyLink"; +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { formatAddress } from "@/utils/address"; +import { Box, Heading, HStack, Spinner, Text, useBreakpointValue, VStack } from "@chakra-ui/react"; +import { getConfig } from "@repo/config"; +import { useMemo } from "react"; +import { useStargateStats } from "../../hooks"; +import { useContractAddresses } from "../../hooks/useContractAddresses"; +import { StargateContractInfo } from "./components/StargateContractInfo"; +import { StargateLevelDetails } from "./components/StargateLevelDetails"; + +const EXPLORER_URL = getConfig(import.meta.env.VITE_APP_ENV).network.explorerUrl; + +export function StargateNodes() { + const { LL } = useI18nContext(); + const { data: stargateStats, isLoading, error } = useStargateStats(); + const { contractAddresses } = useContractAddresses(); + + const stargateNFTContractAddress = useMemo( + () => contractAddresses?.stargateAddress, + [contractAddresses?.stargateAddress], + ); + + const stackSpacing = useBreakpointValue({ + base: 4, + md: 6, + }); + + if (isLoading) { + return ( + + + {LL.admin.stargate_nodes.loading()} + + ); + } + + if (error || !stargateStats) { + return ( + + {LL.admin.stargate_nodes.no_data()} + + ); + } + + return ( + + + + + {LL.admin.stargate_nodes.title()} + + {stargateNFTContractAddress && ( + + {LL.admin.vevote_contract.contract_address()} + + {formatAddress(stargateNFTContractAddress)} + + + )} + + + + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Contracts/VeVoteContract.tsx b/apps/frontend/src/pages/Admin/components/Contracts/VeVoteContract.tsx new file mode 100644 index 00000000..e3655bf7 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Contracts/VeVoteContract.tsx @@ -0,0 +1,67 @@ +import { CopyLink } from "@/components/ui/CopyLink"; +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { formatAddress } from "@/utils/address"; +import { Box, Heading, HStack, Spinner, Text, useBreakpointValue, VStack } from "@chakra-ui/react"; +import { getConfig } from "@repo/config"; +import { useVeVoteInfo } from "../../hooks"; +import { UserRoleChecker } from "../common/UserRoleChecker"; +import { VevoteContractInfo } from "./components/VevoteContractInfo"; + +const EXPLORER_URL = getConfig(import.meta.env.VITE_APP_ENV).network.explorerUrl; +const contractAddress = getConfig(import.meta.env.VITE_APP_ENV).vevoteContractAddress; + +export function VeVoteContract() { + const { LL } = useI18nContext(); + const { data: veVoteInfo, isLoading, error } = useVeVoteInfo(); + + const stackSpacing = useBreakpointValue({ + base: 4, + md: 6, + }); + + if (isLoading) { + return ( + + + {LL.admin.vevote_contract.loading()} + + ); + } + + if (error || !veVoteInfo) { + return ( + + {LL.admin.vevote_contract.no_data()} + + ); + } + + return ( + + + + + {LL.admin.vevote_contract.title()} + + + {LL.admin.vevote_contract.contract_address()} + + {formatAddress(contractAddress)} + + + + + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Contracts/components/NodeManagementContractInfo.tsx b/apps/frontend/src/pages/Admin/components/Contracts/components/NodeManagementContractInfo.tsx new file mode 100644 index 00000000..c36d5c5e --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Contracts/components/NodeManagementContractInfo.tsx @@ -0,0 +1,152 @@ +import { + VStack, + HStack, + Text, + Input, + Button, + FormControl, + Spinner, + Divider, +} from "@chakra-ui/react"; +import { useMemo, useState } from "react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { useUserNodeInfo } from "../../../hooks"; +import { AdminCard } from "../../common/AdminCard"; +import { FormSkeleton } from "@/components/ui/FormSkeleton"; +import { formatAddress } from "@/utils/address"; +import { nodeInfoSchema, type NodeInfoSchema } from "@/schema/adminSchema"; +import { Label } from "@/components/ui/Label"; +import { InputMessage } from "@/components/ui/InputMessage"; +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; + +export function NodeManagementContractInfo() { + const { LL } = useI18nContext(); + const [searchAddress, setSearchAddress] = useState(""); + + const { data: userNodeInfo, isLoading, error } = useUserNodeInfo(searchAddress); + + const handleFormSubmit = async (values: NodeInfoSchema) => { + setSearchAddress(values.userAddress.trim()); + }; + + const nodeData = useMemo(() => { + if (!userNodeInfo) return []; + + const ownedNodes = + userNodeInfo.ownedNodes.length > 0 ? ` (${userNodeInfo.ownedNodes.map(id => id.toString()).join(", ")})` : ""; + const managedNodes = + userNodeInfo.managedNodes.length > 0 ? ` (${userNodeInfo.managedNodes.map(id => id.toString()).join(", ")})` : ""; + return [ + { + label: LL.admin.node_management.is_node_holder(), + value: userNodeInfo.isNodeHolder ? LL.admin.node_management.yes() : LL.admin.node_management.no(), + }, + { + label: LL.admin.node_management.is_node_delegator(), + value: userNodeInfo.isNodeDelegator ? LL.admin.node_management.yes() : LL.admin.node_management.no(), + }, + { + label: LL.admin.node_management.owned_nodes(), + value: `${userNodeInfo.ownedNodes.length}${ownedNodes}`, + }, + { + label: LL.admin.node_management.managed_nodes(), + value: `${userNodeInfo.managedNodes.length}${managedNodes}`, + }, + ]; + }, [userNodeInfo, LL]); + + return ( + + + + {LL.admin.node_management.help_text()} + + + + schema={nodeInfoSchema} + onSubmit={handleFormSubmit} + defaultValues={{ userAddress: "" }}> + {({ register, errors, isValid }) => ( + + + + + + + )} + + + {searchAddress && ( + <> + + + {error && ( + + + {LL.admin.node_management.error({ + error: error instanceof Error ? error.message : LL.admin.unknown_error(), + })} + + + )} + + {isLoading && ( + + + + {LL.admin.node_management.loading_text()} + + + )} + + {!error && userNodeInfo && ( + + + {LL.admin.node_management.results_for({ + address: formatAddress(searchAddress), + })} + + + + {nodeData.map(({ label, value }) => ( + + + {label} + + + {value} + + + ))} + + + )} + + {!error && !userNodeInfo && !isLoading && ( + + {LL.admin.node_management.no_results()} + + )} + + )} + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Contracts/components/StargateContractInfo.tsx b/apps/frontend/src/pages/Admin/components/Contracts/components/StargateContractInfo.tsx new file mode 100644 index 00000000..1972c526 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Contracts/components/StargateContractInfo.tsx @@ -0,0 +1,44 @@ +import { VStack, HStack, Text } from "@chakra-ui/react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { AdminCard } from "../../common/AdminCard"; +import { useMemo } from "react"; + +interface StargateContractInfoProps { + totalSupply: bigint; + levelIds: number[]; +} + +export function StargateContractInfo({ totalSupply, levelIds }: StargateContractInfoProps) { + const { LL } = useI18nContext(); + + const contractData = useMemo( + () => [ + { + label: LL.admin.stargate_nodes.total_supply(), + value: totalSupply.toString(), + }, + { + label: LL.admin.stargate_nodes.available_levels(), + value: `${levelIds.length} (${levelIds.join(", ")})`, + }, + ], + [LL, totalSupply, levelIds], + ); + + return ( + + + {contractData.map(({ label, value }) => ( + + + {label} + + + {value} + + + ))} + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Contracts/components/StargateLevelDetails.tsx b/apps/frontend/src/pages/Admin/components/Contracts/components/StargateLevelDetails.tsx new file mode 100644 index 00000000..57dd7a36 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Contracts/components/StargateLevelDetails.tsx @@ -0,0 +1,79 @@ +import { DataTable } from "@/components/ui/TableSkeleton"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { TranslationFunctions } from "@/i18n/i18n-types"; +import { Box, Heading } from "@chakra-ui/react"; +import { createColumnHelper } from "@tanstack/react-table"; +import { useMemo } from "react"; +import { veVoteService } from "../../../services/VeVoteService"; +import { BaseCell, NumberCell, TableHeader, VETAmountCell } from "../../common/AdminTableCells"; + +interface Level { + name: string; + maturityBlocks: bigint; + vetAmountRequiredToStake: bigint; +} + +interface StargateLevelDetailsProps { + levels: Level[]; + levelIds: number[]; +} + +export interface StargateLevelRow { + levelId: number; + name: string; + maturityBlocks: bigint; + vetAmountRequiredToStake: bigint; +} + +const columnHelper = createColumnHelper(); + +const columns = (LL: TranslationFunctions) => [ + columnHelper.accessor("name", { + cell: data => , + header: () => , + id: "NAME", + size: 160, + }), + columnHelper.accessor("maturityBlocks", { + cell: data => , + header: () => , + id: "MATURITY_BLOCKS", + size: 140, + }), + columnHelper.accessor("vetAmountRequiredToStake", { + cell: data => , + header: () => , + id: "VET_REQUIRED", + size: 140, + }), +]; + +export function StargateLevelDetails({ levels, levelIds }: StargateLevelDetailsProps) { + const { LL } = useI18nContext(); + + const tableData: StargateLevelRow[] = useMemo(() => { + const stargateRows = levels.map((level, index) => ({ + levelId: levelIds[index], + name: level.name, + maturityBlocks: level.maturityBlocks, + vetAmountRequiredToStake: level.vetAmountRequiredToStake, + })); + + return [veVoteService.getValidatorInfo(), ...stargateRows]; + }, [levels, levelIds]); + + if (levels.length === 0) { + return null; + } + + return ( + + + {LL.admin.stargate_nodes.level_details_title()} + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Contracts/components/VevoteContractInfo.tsx b/apps/frontend/src/pages/Admin/components/Contracts/components/VevoteContractInfo.tsx new file mode 100644 index 00000000..cfa2c10a --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Contracts/components/VevoteContractInfo.tsx @@ -0,0 +1,67 @@ +import { VStack, HStack, Text } from "@chakra-ui/react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { useFormatTime } from "@/hooks/useFormatTime"; +import { VeVoteInfo } from "../../../services"; +import { AdminCard } from "../../common/AdminCard"; +import { FixedPointNumber, Units } from "@vechain/sdk-core"; +import { useMemo } from "react"; + +interface VevoteContractInfoProps { + veVoteInfo: VeVoteInfo; +} + +export function VevoteContractInfo({ veVoteInfo }: VevoteContractInfoProps) { + const { LL } = useI18nContext(); + const { formatTime } = useFormatTime(); + + const contractData = useMemo( + () => [ + { + label: LL.admin.vevote_contract.contract_version(), + value: veVoteInfo.version.toString(), + }, + { + label: LL.admin.vevote_contract.quorum_numerator(), + value: `${veVoteInfo.quorumNumerator.toString()} (${((Number(veVoteInfo.quorumNumerator) / Number(veVoteInfo.quorumDenominator)) * 100).toFixed(1)}%)`, + }, + { + label: LL.admin.vevote_contract.quorum_denominator(), + value: veVoteInfo.quorumDenominator.toString(), + }, + { + label: LL.admin.vevote_contract.min_voting_delay(), + value: `${veVoteInfo.minVotingDelay} (${formatTime(veVoteInfo.minVotingDelay * 10)})`, + }, + { + label: LL.admin.vevote_contract.min_voting_duration(), + value: `${veVoteInfo.minVotingDuration} (${formatTime(veVoteInfo.minVotingDuration * 10)})`, + }, + { + label: LL.admin.vevote_contract.max_voting_duration(), + value: `${veVoteInfo.maxVotingDuration} (${formatTime(veVoteInfo.maxVotingDuration * 10)})`, + }, + { + label: LL.admin.vevote_contract.min_staked_amount(), + value: LL.admin.vet_format({ amount: Units.formatEther(FixedPointNumber.of(veVoteInfo.minStakedAmount)) }), + }, + ], + [LL.admin, formatTime, veVoteInfo], + ); + + return ( + + + {contractData.map(({ label, value }) => ( + + + {label} + + + {value} + + + ))} + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Users/RoleUsersCard.tsx b/apps/frontend/src/pages/Admin/components/Users/RoleUsersCard.tsx new file mode 100644 index 00000000..d53f7706 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Users/RoleUsersCard.tsx @@ -0,0 +1,234 @@ +import { VStack, HStack, Text, Select, Button, FormControl, Spinner, Box, Badge, Divider } from "@chakra-ui/react"; +import { useState, useCallback, useMemo } from "react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { AdminCard } from "../common/AdminCard"; +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; +import { CopyLink } from "@/components/ui/CopyLink"; +import { getConfig } from "@repo/config"; +import { FormSkeleton } from "@/components/ui/FormSkeleton"; +import { Label } from "@/components/ui/Label"; +import { InputMessage } from "@/components/ui/InputMessage"; +import { formatAddress } from "@/utils/address"; +import { veVoteService, type RoleUserInfo } from "../../services/VeVoteService"; +import { useThor } from "@vechain/vechain-kit"; +import { ROLES } from "./UserManagementCard"; +import { executeCall } from "@/utils/contract"; +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { z } from "zod"; + +const EXPLORER_URL = getConfig(import.meta.env.VITE_APP_ENV).network.explorerUrl; +const contractAddress = getConfig(import.meta.env.VITE_APP_ENV).vevoteContractAddress; +const contractInterface = VeVote__factory.createInterface(); + +const MAX_VISIBLE_USERS = 6; + +const roleUsersQuerySchema = z.object({ + selectedRole: z.enum(ROLES).or(z.literal("")), +}); + +type RoleUsersQuerySchema = z.infer; + +export function RoleUsersCard() { + const { LL } = useI18nContext(); + const thor = useThor(); + const [isLoading, setIsLoading] = useState(false); + const [roleUsers, setRoleUsers] = useState([]); + const [selectedRole, setSelectedRole] = useState<(typeof ROLES)[number] | "">(""); + const [error, setError] = useState(null); + + const defaultValues = useMemo( + () => ({ + selectedRole: "" as const, + }), + [], + ); + + const onSubmit = useCallback( + async (values: RoleUsersQuerySchema) => { + if (!thor) { + setError("Please connect your wallet"); + return; + } + + setIsLoading(true); + setError(null); + setSelectedRole(values.selectedRole); + + try { + const roleRes = await executeCall({ + contractAddress, + contractInterface, + method: values.selectedRole as (typeof ROLES)[number], + args: [], + }); + + if (!roleRes.success) { + throw new Error(LL.admin.unknown_error()); + } + + const roleHash = roleRes.result.plain as string; + const users = await veVoteService.getRoleUsers(thor, roleHash); + setRoleUsers(users); + } catch (err) { + const error = err as Error; + setError(error.message); + setRoleUsers([]); + } finally { + setIsLoading(false); + } + }, + [thor, LL], + ); + + const hasQueried = useMemo(() => Boolean(selectedRole), [selectedRole]); + const showScrollableList = useMemo(() => roleUsers.length > MAX_VISIBLE_USERS, [roleUsers.length]); + + return ( + + + + {LL.admin.role_users.help_text()} + + + + {({ register, errors, watch }) => { + const isButtonDisabled = watch("selectedRole") === ""; + return ( + + + + + + + ); + }} + + + {hasQueried && ( + <> + + + {error && ( + + {LL.admin.role_users.error_description({ error })} + + )} + + {isLoading && ( + + + + {LL.admin.role_users.loading_text()} + + + )} + + {!error && !isLoading && roleUsers.length === 0 && ( + + {LL.admin.role_users.no_users()} + + )} + + {!error && !isLoading && roleUsers.length > 0 && ( + + + + {LL.admin.role_users.results_title()} + + + {LL.admin.role_users.user_count({ count: roleUsers.length })} + + + + + {LL.admin.role_users.role_selected({ + role: LL.admin.common_roles[selectedRole as (typeof ROLES)[number]](), + })} + + + + + {showScrollableList && ( + + {LL.admin.role_users.scrollable_hint()} + + )} + + )} + + )} + + + ); +} + +const UserWithRoleList = ({ + roleUsers, + showScrollableList, +}: { + roleUsers: RoleUserInfo[]; + showScrollableList: boolean; +}) => { + const { LL } = useI18nContext(); + return ( + + + {roleUsers.map((user, index) => ( + + + + {formatAddress(user.address)} + + + {LL.admin.role_users.granted_at({ date: user.grantedAt.toLocaleDateString() })} + + + + {LL.admin.role_users.view_tx()} + + + ))} + + + ); +}; diff --git a/apps/frontend/src/pages/Admin/components/Users/UserManagementCard.tsx b/apps/frontend/src/pages/Admin/components/Users/UserManagementCard.tsx new file mode 100644 index 00000000..cb7bb7df --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Users/UserManagementCard.tsx @@ -0,0 +1,250 @@ +import { FormSkeleton, FormSkeletonProps } from "@/components/ui/FormSkeleton"; +import { InputMessage } from "@/components/ui/InputMessage"; +import { Label } from "@/components/ui/Label"; +import { MessageModal } from "@/components/ui/ModalSkeleton"; +import { useRoleManagement } from "@/hooks/useRoleManagement"; +import { useUserAdminRoles } from "@/hooks/useAdminUserRoles"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { CheckIcon } from "@/icons"; +import { userManagementSchema, type UserManagementSchema } from "@/schema/adminSchema"; +import { executeCall } from "@/utils/contract"; +import { isValidAddress } from "@/utils/zod"; +import { + Badge, + Box, + Button, + Divider, + FormControl, + HStack, + Input, + Select, + Spinner, + Text, + useDisclosure, + VStack, + Wrap, + WrapItem, +} from "@chakra-ui/react"; +import { getConfig } from "@repo/config"; +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { useCallback, useMemo } from "react"; +import { AdminCard } from "../common/AdminCard"; +import { SensitiveWarning } from "../common/SensitiveWarning"; + +export const ROLES = [ + "DEFAULT_ADMIN_ROLE", + "EXECUTOR_ROLE", + "SETTINGS_MANAGER_ROLE", + "NODE_WEIGHT_MANAGER_ROLE", + "UPGRADER_ROLE", + "WHITELISTED_ROLE", + "WHITELIST_ADMIN_ROLE", +] as const; + +const contractAddress = getConfig(import.meta.env.VITE_APP_ENV).vevoteContractAddress; +const contractInterface = VeVote__factory.createInterface(); + +export function UserManagementCard() { + const { LL } = useI18nContext(); + const { sendTransaction, isTransactionPending } = useRoleManagement(); + + const { isOpen: isSuccessOpen, onClose: onSuccessClose, onOpen: onSuccessOpen } = useDisclosure(); + + const defaultValues = useMemo( + () => ({ + userAddress: "", + selectedRole: "", + }), + [], + ); + + const handleRoleAction = useCallback( + async (action: "grant" | "revoke", values: UserManagementSchema) => { + const nodeRes = await executeCall({ + contractAddress, + contractInterface, + method: values.selectedRole as (typeof ROLES)[number], + args: [], + }); + + if (!nodeRes.success) { + throw new Error(LL.admin.common_roles.error_description({ error: LL.admin.unknown_error() })); + } + + const role = nodeRes.result.plain as (typeof ROLES)[number]; + + await sendTransaction({ + action, + role, + account: values.userAddress, + }); + + onSuccessOpen(); + }, + [LL.admin, sendTransaction, onSuccessOpen], + ); + + const handleFormSubmit: FormSkeletonProps["onSubmit"] = useCallback( + async (values, _, action) => { + try { + const roleAction = action === "revoke" ? "revoke" : "grant"; + await handleRoleAction(roleAction, values); + } catch (err) { + const error = err as Error; + console.error(error); + } + }, + [handleRoleAction], + ); + + return ( + <> + + + {LL.admin.user_management.description()} + + + + schema={userManagementSchema} + onSubmit={handleFormSubmit} + defaultValues={defaultValues}> + {({ register, errors, watch, isValid }) => { + const watchedAddress = watch("userAddress"); + + return ( + + + + + + + + + + + + + + + + + + + + ); + }} + + + + + + + + {LL.admin.common_roles.grant_success_description()} + + + + ); +} + +const UserRolesSection = ({ userAddress }: { userAddress: string }) => { + const { LL } = useI18nContext(); + const { data: userRoles, isLoading: isLoadingRoles } = useUserAdminRoles( + userAddress && isValidAddress(userAddress) ? userAddress : "", + ); + + const isUserRoles = useMemo(() => Boolean(userRoles && userRoles.length > 0), [userRoles]); + + if (!userAddress || !isValidAddress(userAddress)) { + return null; + } + + return ( + + + {LL.admin.user_management.current_roles_label()} + + + + ); +}; + +const RoleSection = ({ + isLoadingRoles, + isUserRoles, + userRoles, +}: { + isLoadingRoles: boolean; + isUserRoles: boolean; + userRoles?: (typeof ROLES)[number][]; +}) => { + const { LL } = useI18nContext(); + if (isLoadingRoles) { + return ( + + + + {LL.admin.user_management.checking_roles()} + + + ); + } + + if (isUserRoles) { + return ( + + {userRoles?.map(role => ( + + + {LL.admin.common_roles[role]()} + + + ))} + + ); + } + + return ( + + {LL.admin.user_management.no_roles_assigned()} + + ); +}; diff --git a/apps/frontend/src/pages/Admin/components/Users/VotingPowerAtTimepointCard.tsx b/apps/frontend/src/pages/Admin/components/Users/VotingPowerAtTimepointCard.tsx new file mode 100644 index 00000000..3c928d55 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Users/VotingPowerAtTimepointCard.tsx @@ -0,0 +1,213 @@ +import { Box, Button, FormControl, Input, Text, VStack, HStack, Divider } from "@chakra-ui/react"; +import { useState, useCallback, useMemo } from "react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { AdminCard } from "../common/AdminCard"; +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; +import { useVotingPowerAtTimepoint, VotingPowerAtTimepointResult } from "../../hooks/useVotingPowerAtTimepoint"; +import { useVechainDomainOrAddress } from "@/hooks/useVechainDomainOrAddress"; +import { CopyLink } from "@/components/ui/CopyLink"; +import { getConfig } from "@repo/config"; +import { FormSkeleton } from "@/components/ui/FormSkeleton"; +import { Label } from "@/components/ui/Label"; +import { InputMessage } from "@/components/ui/InputMessage"; +import { votingPowerQuerySchema, VotingPowerQuerySchema } from "@/schema/adminSchema"; +import { formatAddress } from "@/utils/address"; +import { formatVotingPower } from "@/utils/proposals/helpers"; + +const EXPLORER_URL = getConfig(import.meta.env.VITE_APP_ENV).network.explorerUrl; + +export function VotingPowerAtTimepointCard() { + const { LL } = useI18nContext(); + + const [queryParams, setQueryParams] = useState<{ + address?: string; + timepoint?: number; + masterAddress?: string; + }>({ address: undefined, timepoint: undefined, masterAddress: undefined }); + + const defaultValues = useMemo( + () => ({ + address: "", + timepoint: undefined, + masterAddress: "", + }), + [], + ); + + const onSubmit = useCallback(async (values: VotingPowerQuerySchema) => { + setQueryParams({ + address: values.address, + timepoint: values.timepoint, + masterAddress: values.masterAddress || undefined, + }); + }, []); + + const { data: votingPowerData, isLoading, error } = useVotingPowerAtTimepoint(queryParams); + + return ( + + + + {LL.admin.voting_power_timepoint.help_text()} + + + + {({ register, errors }) => ( + + + + + + + + + + + + + )} + + + + + ); +} + +const QueryResults = ({ + queryParams, + error, + votingPowerData, + isLoading, +}: { + queryParams: { address?: string; timepoint?: number; masterAddress?: string }; + error: Error | null; + votingPowerData?: VotingPowerAtTimepointResult; + isLoading: boolean; +}) => { + const { LL } = useI18nContext(); + + const { addressOrDomain } = useVechainDomainOrAddress(queryParams.address); + + const hasQueried = useMemo(() => Boolean(queryParams.address && queryParams.timepoint !== undefined), [queryParams]); + + if (!hasQueried) return null; + return ( + <> + + + {error && ( + + + {LL.admin.voting_power_timepoint.error_description({ + error: error instanceof Error ? error.message : LL.admin.unknown_error(), + })} + + + )} + + {!error && votingPowerData && ( + + + {LL.admin.voting_power_timepoint.results_title()} + + + + + {LL.admin.voting_power_timepoint.address()} + + {addressOrDomain} + + + + + {LL.admin.voting_power_timepoint.timepoint()} + {queryParams.timepoint?.toString()} + + + {queryParams.masterAddress && ( + + {LL.admin.voting_power_timepoint.master_address()} + + {formatAddress(queryParams.masterAddress)} + + + )} + + + + + + {LL.admin.voting_power_timepoint.node_power_label()} + + {formatVotingPower(votingPowerData.nodePower)} + + + + + {LL.admin.voting_power_timepoint.validator_power_label()} + + {formatVotingPower(votingPowerData.validatorPower)} + + + + + + + {LL.admin.voting_power_timepoint.total_power_label()} + + + {formatVotingPower(votingPowerData.totalPower)} + + + + + )} + + {!error && !votingPowerData && !isLoading && ( + + {LL.admin.voting_power_timepoint.no_results()} + + )} + + ); +}; diff --git a/apps/frontend/src/pages/Admin/components/Utils/GovernanceSettings.tsx b/apps/frontend/src/pages/Admin/components/Utils/GovernanceSettings.tsx new file mode 100644 index 00000000..f1915266 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Utils/GovernanceSettings.tsx @@ -0,0 +1,42 @@ +import { Flex, Text, useDisclosure } from "@chakra-ui/react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { useVeVoteInfo } from "../../hooks"; +import { MessageModal } from "@/components/ui/ModalSkeleton"; +import { CheckIcon } from "@/icons"; +import { GovernanceSettingsForm } from "./GovernanceSettingsForm"; +import { LevelMultipliersCard } from "./LevelMultipliersCard"; +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; + +export function GovernanceSettings() { + const { LL } = useI18nContext(); + const { data: veVoteInfo, isLoading, error } = useVeVoteInfo(); + + const { isOpen: isSuccessOpen, onClose: onSuccessClose, onOpen: onSuccessOpen } = useDisclosure(); + + if (isLoading) return {LL.admin.vevote_contract.loading()}; + + if (error || !veVoteInfo) { + return ( + + {LL.admin.vevote_contract.no_data()} + + ); + } + + return ( + + + + + + {LL.admin.governance_settings.success_description()} + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Utils/GovernanceSettingsForm.tsx b/apps/frontend/src/pages/Admin/components/Utils/GovernanceSettingsForm.tsx new file mode 100644 index 00000000..b2d9853f --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Utils/GovernanceSettingsForm.tsx @@ -0,0 +1,179 @@ +import { FormSkeleton } from "@/components/ui/FormSkeleton"; +import { InputMessage } from "@/components/ui/InputMessage"; +import { Label } from "@/components/ui/Label"; +import { useFormatTime } from "@/hooks/useFormatTime"; +import { useGovernanceSettings } from "@/hooks/useGovernanceSettings"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { governanceSettingsSchema, GovernanceSettingsSchema } from "@/schema/adminSchema"; +import { Button, Flex, FormControl, NumberInput, NumberInputField, SimpleGrid, Text } from "@chakra-ui/react"; +import { FixedPointNumber, Units } from "@vechain/sdk-core"; +import { useCallback, useMemo } from "react"; +import { VeVoteInfo } from "../../services"; +import { AdminCard } from "../common/AdminCard"; +import { SensitiveWarning } from "../common/SensitiveWarning"; + +interface GovernanceSettingsFormProps { + veVoteInfo: VeVoteInfo; + onSuccess?: () => void; +} + +export function GovernanceSettingsForm({ veVoteInfo, onSuccess }: GovernanceSettingsFormProps) { + const { LL } = useI18nContext(); + const { sendTransaction, isTransactionPending } = useGovernanceSettings(); + const { formatTime } = useFormatTime(); + + const defaultValues = useMemo( + () => ({ + quorumNumerator: undefined, + minVotingDelay: undefined, + minVotingDuration: undefined, + maxVotingDuration: undefined, + minStakedVetAmount: undefined, + }), + [], + ); + + const onSubmit = useCallback( + async (values: GovernanceSettingsSchema) => { + const { quorumNumerator, minVotingDelay, minVotingDuration, maxVotingDuration, minStakedVetAmount } = values; + const updateParams: { + updateQuorumNumerator?: number; + setMinVotingDelay?: number; + setMinVotingDuration?: number; + setMaxVotingDuration?: number; + setMinStakedVetAmount?: bigint; + } = {}; + + if (quorumNumerator && quorumNumerator !== Number(veVoteInfo.quorumNumerator)) { + updateParams.updateQuorumNumerator = quorumNumerator; + } + + if (minVotingDelay && minVotingDelay !== veVoteInfo.minVotingDelay) { + updateParams.setMinVotingDelay = minVotingDelay; + } + + if (minVotingDuration && minVotingDuration !== veVoteInfo.minVotingDuration) { + updateParams.setMinVotingDuration = minVotingDuration; + } + + if (maxVotingDuration && maxVotingDuration !== veVoteInfo.maxVotingDuration) { + updateParams.setMaxVotingDuration = maxVotingDuration; + } + + if (minStakedVetAmount) { + // Convert VET to wei using parseEther + const newValue = BigInt(Units.parseEther(minStakedVetAmount.toString()).toString()); + if (newValue !== veVoteInfo.minStakedAmount) { + updateParams.setMinStakedVetAmount = newValue; + } + } + + if (Object.keys(updateParams).length === 0) return; + + await sendTransaction(updateParams); + onSuccess?.(); + }, + [veVoteInfo, sendTransaction, onSuccess], + ); + + return ( + + + {LL.admin.governance_settings.description()} + + + + {({ register, errors, isDirty }) => { + return ( + + + + + + + + + + + + + + + + + + + + + + ); + }} + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Utils/LevelMultipliersCard.tsx b/apps/frontend/src/pages/Admin/components/Utils/LevelMultipliersCard.tsx new file mode 100644 index 00000000..41b1f8a4 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Utils/LevelMultipliersCard.tsx @@ -0,0 +1,205 @@ +import { FormSkeleton } from "@/components/ui/FormSkeleton"; +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; +import { DataTable } from "@/components/ui/TableSkeleton"; +import { useLevelMultipliers } from "@/hooks/useLevelMultipliers"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { Button, Flex, Text } from "@chakra-ui/react"; +import { createColumnHelper } from "@tanstack/react-table"; +import { useCallback, useMemo } from "react"; +import { z } from "zod"; +import { useLevelMultipliersInfo } from "../../hooks/useLevelMultipliersInfo"; +import { AdminCard } from "../common/AdminCard"; +import { BaseCell, MultiplierInputCell, TableHeader } from "../common/AdminTableCells"; +import { SensitiveWarning } from "../common/SensitiveWarning"; + +interface LevelMultipliersCardProps { + onSuccess?: () => void; +} + +const getDefaultMultiplierForLevel = (levelId: number): number => { + const defaults = [200, 100, 100, 100, 150, 150, 150, 150, 100, 100, 100]; + return defaults[levelId] || 100; +}; + +const multiplierSchema = z.object({ + multiplier0: z.coerce.number().min(0).max(1000).optional(), + multiplier1: z.coerce.number().min(0).max(1000).optional(), + multiplier2: z.coerce.number().min(0).max(1000).optional(), + multiplier3: z.coerce.number().min(0).max(1000).optional(), + multiplier4: z.coerce.number().min(0).max(1000).optional(), + multiplier5: z.coerce.number().min(0).max(1000).optional(), + multiplier6: z.coerce.number().min(0).max(1000).optional(), + multiplier7: z.coerce.number().min(0).max(1000).optional(), + multiplier8: z.coerce.number().min(0).max(1000).optional(), + multiplier9: z.coerce.number().min(0).max(1000).optional(), + multiplier10: z.coerce.number().min(0).max(1000).optional(), +}); + +type MultiplierSchema = z.infer; + +interface LevelMultiplierRow { + id: number; + name: string; + currentMultiplier: number; +} + +const columnHelper = createColumnHelper(); + +export function LevelMultipliersCard({ onSuccess }: LevelMultipliersCardProps) { + const { LL } = useI18nContext(); + const { sendTransaction, isTransactionPending } = useLevelMultipliers(); + const { + data: currentMultipliers, + isLoading: isLoadingMultipliers, + error: multipliersError, + } = useLevelMultipliersInfo(); + + const defaultValues = useMemo( + () => ({ + multiplier0: undefined, + multiplier1: undefined, + multiplier2: undefined, + multiplier3: undefined, + multiplier4: undefined, + multiplier5: undefined, + multiplier6: undefined, + multiplier7: undefined, + multiplier8: undefined, + multiplier9: undefined, + multiplier10: undefined, + }), + [], + ); + + const onSubmit = useCallback( + async (values: MultiplierSchema) => { + const multipliers = Array.from({ length: 11 }, (_, index) => { + const fieldKey = `multiplier${index}` as keyof MultiplierSchema; + const formValue = values[fieldKey]; + + return formValue !== undefined + ? formValue + : (currentMultipliers?.[index] ?? getDefaultMultiplierForLevel(index)); + }); + + await sendTransaction({ + updateLevelIdMultipliers: multipliers, + }); + + onSuccess?.(); + }, + [currentMultipliers, sendTransaction, onSuccess], + ); + + const levelData = useMemo( + () => [ + { id: 0, name: LL.admin.governance_settings.validator_multiplier() }, + { id: 1, name: LL.admin.governance_settings.strength() }, + { id: 2, name: LL.admin.governance_settings.thunder() }, + { id: 3, name: LL.admin.governance_settings.mjolnir() }, + { id: 4, name: LL.admin.governance_settings.vethor_x() }, + { id: 5, name: LL.admin.governance_settings.strength_x() }, + { id: 6, name: LL.admin.governance_settings.thunder_x() }, + { id: 7, name: LL.admin.governance_settings.mjolnir_x() }, + { id: 8, name: LL.admin.governance_settings.dawn() }, + { id: 9, name: LL.admin.governance_settings.lightning() }, + { id: 10, name: LL.admin.governance_settings.flash() }, + ], + [LL.admin.governance_settings], + ); + + const getCurrentMultiplier = useCallback( + (index: number) => { + return currentMultipliers?.[index] ?? getDefaultMultiplierForLevel(index); + }, + [currentMultipliers], + ); + + const tableData: LevelMultiplierRow[] = useMemo( + () => + levelData.map(level => ({ + ...level, + currentMultiplier: getCurrentMultiplier(level.id), + })), + [levelData, getCurrentMultiplier], + ); + + const columns = useMemo( + () => [ + columnHelper.accessor("name", { + cell: data => , + header: () => , + id: "NODE_NAME", + size: 200, + }), + columnHelper.accessor("currentMultiplier", { + cell: data => , + header: () => , + id: "CURRENT_MULTIPLIER", + size: 140, + }), + columnHelper.accessor("id", { + cell: data => ( + + ), + header: () => , + id: "NEW_MULTIPLIER", + size: 120, + }), + ], + [LL.admin.governance_settings], + ); + + if (isLoadingMultipliers) { + return ( + + {LL.admin.vevote_contract.loading()} + + ); + } + + if (multipliersError) { + return ( + + + Error loading multipliers + + + ); + } + + return ( + + + {LL.admin.governance_settings.level_multipliers_help()} + + + + {({ watch }) => { + const watchedValues = watch(); + const hasChanges = Object.values(watchedValues).some( + value => value !== undefined && value !== null && value !== "", + ); + + return ( + + + + + ); + }} + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/Utils/UserManagement.tsx b/apps/frontend/src/pages/Admin/components/Utils/UserManagement.tsx new file mode 100644 index 00000000..ccbe2355 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/Utils/UserManagement.tsx @@ -0,0 +1,25 @@ +import { Flex, useBreakpointValue } from "@chakra-ui/react"; +import { RoleUsersCard } from "../Users/RoleUsersCard"; +import { UserManagementCard } from "../Users/UserManagementCard"; +import { VotingPowerAtTimepointCard } from "../Users/VotingPowerAtTimepointCard"; + +export function UserManagement() { + const stackSpacing = useBreakpointValue({ + base: 4, + md: 6, + }); + + return ( + + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/common/AdminCard.tsx b/apps/frontend/src/pages/Admin/components/common/AdminCard.tsx new file mode 100644 index 00000000..b825fc43 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/common/AdminCard.tsx @@ -0,0 +1,42 @@ +import { Box, Heading, BoxProps, useBreakpointValue } from "@chakra-ui/react"; +import { ReactNode } from "react"; + +interface AdminCardProps extends BoxProps { + title: string; + children: ReactNode; +} + +export function AdminCard({ title, children, ...boxProps }: AdminCardProps) { + const cardWidth = useBreakpointValue({ + base: "100%", + md: "fit-content", + }); + + const width = useBreakpointValue({ + base: "auto", + md: 400, + }); + + const padding = useBreakpointValue({ + base: 3, + md: 4, + }); + + return ( + + + {title} + + {children} + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/common/AdminTableCells.tsx b/apps/frontend/src/pages/Admin/components/common/AdminTableCells.tsx new file mode 100644 index 00000000..fd098070 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/common/AdminTableCells.tsx @@ -0,0 +1,68 @@ +import { Text, TextProps, FormControl, Input } from "@chakra-ui/react"; +import { FixedPointNumber, Units } from "@vechain/sdk-core"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { useFormContext } from "react-hook-form"; + +export const TableHeader = ({ label }: { label: string }) => { + return ( + + {label} + + ); +}; + +export const BaseCell = ({ value, ...restProps }: TextProps & { value: string | number }) => { + return ( + + {value} + + ); +}; + +export const VETAmountCell = ({ amount }: { amount: bigint }) => { + const { LL } = useI18nContext(); + + return ( + + ); +}; + +export const NumberCell = ({ value, suffix }: { value: number | bigint; suffix?: string }) => { + const displayValue = typeof value === "bigint" ? value.toString() : value.toLocaleString(); + return ; +}; + +export const MultiplierInputCell = ({ fieldName, placeholder }: { fieldName: string; placeholder: string }) => { + const { + register, + formState: { errors }, + } = useFormContext(); + + return ( + + + + ); +}; diff --git a/apps/frontend/src/pages/Admin/components/common/SensitiveWarning.tsx b/apps/frontend/src/pages/Admin/components/common/SensitiveWarning.tsx new file mode 100644 index 00000000..cbf4b40f --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/common/SensitiveWarning.tsx @@ -0,0 +1,14 @@ +import { GenericInfoBox } from "@/components/ui/GenericInfoBox"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { Text } from "@chakra-ui/react"; + +export const SensitiveWarning = () => { + const { LL } = useI18nContext(); + return ( + + + {LL.admin.sensitive_operation_warning()} + + + ); +}; diff --git a/apps/frontend/src/pages/Admin/components/common/UserRoleChecker.tsx b/apps/frontend/src/pages/Admin/components/common/UserRoleChecker.tsx new file mode 100644 index 00000000..a40c6133 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/common/UserRoleChecker.tsx @@ -0,0 +1,59 @@ +import { useI18nContext } from "@/i18n/i18n-react"; +import { CheckIcon, CircleXIcon } from "@/icons"; +import { HStack, Icon, Spinner, Text, VStack } from "@chakra-ui/react"; +import { useContractRoles } from "@/hooks/useContractRoles"; +import { useWallet } from "@vechain/vechain-kit"; +import { ContractType } from "@/pages/Admin/constants/contracts"; +import { AdminCard } from "./AdminCard"; + +interface UserRoleCheckerProps { + contractType?: ContractType; +} + +export function UserRoleChecker({ contractType }: UserRoleCheckerProps) { + const { LL } = useI18nContext(); + const { account } = useWallet(); + const { defaultRoles, roles, isLoading } = useContractRoles(contractType); + + if (!account?.address) { + return ( + + + {LL.admin.user_role_checker.connect_wallet_message()} + + + ); + } + + if (isLoading) { + return ( + + + + + {LL.admin.user_role_checker.checking_roles()} + + + + ); + } + + return ( + + + {defaultRoles.map(role => { + const hasRole = roles?.includes(role) || false; + + return ( + + + {LL.admin.common_roles[role]()} + + + + ); + })} + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/navigation/MobileNavDrawer.tsx b/apps/frontend/src/pages/Admin/components/navigation/MobileNavDrawer.tsx new file mode 100644 index 00000000..ca098440 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/navigation/MobileNavDrawer.tsx @@ -0,0 +1,133 @@ +import { + Drawer, + DrawerBody, + DrawerCloseButton, + DrawerContent, + DrawerHeader, + DrawerOverlay, + VStack, + Text, + Box, + Accordion, + AccordionItem, + AccordionButton, + AccordionPanel, + Button, + useColorModeValue, +} from "@chakra-ui/react"; +import { ChevronDownIcon } from "@/icons"; +import { useI18nContext } from "@/i18n/i18n-react"; + +interface MobileNavDrawerProps { + isOpen: boolean; + onClose: () => void; + activeMainTab: number; + activeSubTab: number; + onMainTabChange: (index: number) => void; + onSubTabChange: (index: number) => void; +} + +export function MobileNavDrawer({ + isOpen, + onClose, + activeMainTab, + activeSubTab, + onMainTabChange, + onSubTabChange, +}: MobileNavDrawerProps) { + const { LL } = useI18nContext(); + + const bgColor = useColorModeValue("white", "gray.800"); + const borderColor = useColorModeValue("gray.200", "gray.600"); + + const contractsSubTabs = [ + { label: LL.admin.contracts.vevote(), index: 0, id: "vevote" }, + { label: LL.admin.contracts.node_management(), index: 1, id: "node-management" }, + { label: LL.admin.contracts.stargate_nodes(), index: 2, id: "stargate-nodes" }, + ]; + + const utilsSubTabs = [ + { label: LL.admin.tabs.users(), index: 0, id: "users" }, + { label: LL.admin.tabs.governance_settings(), index: 1, id: "governance-settings" }, + ]; + + const handleNavigation = (mainTabIndex: number, subTabIndex: number) => { + onMainTabChange(mainTabIndex); + onSubTabChange(subTabIndex); + onClose(); + }; + + return ( + + + + + + {LL.admin.title()} + + + + + {/* Contracts Section */} + + + + {LL.admin.tabs.contracts()} + + + + + + {contractsSubTabs.map(tab => ( + + ))} + + + + + {/* Utils Section */} + + + + {LL.admin.tabs.utils()} + + + + + + {utilsSubTabs.map(tab => ( + + ))} + + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/navigation/NavigationComponents.tsx b/apps/frontend/src/pages/Admin/components/navigation/NavigationComponents.tsx new file mode 100644 index 00000000..ae71a78c --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/navigation/NavigationComponents.tsx @@ -0,0 +1,86 @@ +import { Box, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; +import { useI18nContext } from "@/i18n/i18n-react"; +import { useMemo } from "react"; + +const contractKeys = ["vevote-contract", "node-management", "stargate-nodes"]; +const utilsKeys = ["user-management", "governance-settings"]; + +interface MainTabsProps { + mainTabIndex: number; + onMainTabChange: (index: number) => void; + children: React.ReactNode; +} + +export function MainTabs({ mainTabIndex, onMainTabChange, children }: MainTabsProps) { + const { LL } = useI18nContext(); + + return ( + + + {LL.admin.tabs.contracts()} + {LL.admin.tabs.utils()} + + {children} + + ); +} + +interface HorizontalTabsProps { + tabIndex: number; + onTabChange: (index: number) => void; + content: React.ReactNode[]; + type: "contracts" | "utils"; +} + +export function HorizontalTabs({ tabIndex, onTabChange, content, type }: HorizontalTabsProps) { + const { LL } = useI18nContext(); + const keys = useMemo(() => (type === "contracts" ? contractKeys : utilsKeys), [type]); + const tabLabels = useMemo( + () => + type === "contracts" + ? [LL.admin.contracts.vevote(), LL.admin.contracts.node_management(), LL.admin.contracts.stargate_nodes()] + : [LL.admin.tabs.users(), LL.admin.tabs.governance_settings()], + [LL, type], + ); + + return ( + + + {tabLabels.map(label => ( + {label} + ))} + + + + {content.map((item, index) => ( + + {item} + + ))} + + + ); +} + +interface MobileContentProps { + mainTabIndex: number; + contractsTabIndex: number; + utilsTabIndex: number; + contractsContent: React.ReactNode[]; + utilsContent: React.ReactNode[]; +} + +export function MobileContent({ + mainTabIndex, + contractsTabIndex, + utilsTabIndex, + contractsContent, + utilsContent, +}: MobileContentProps) { + const currentContent = useMemo( + () => (mainTabIndex === 0 ? contractsContent[contractsTabIndex] : utilsContent[utilsTabIndex]), + [mainTabIndex, contractsTabIndex, utilsTabIndex, contractsContent, utilsContent], + ); + + return {currentContent}; +} diff --git a/apps/frontend/src/pages/Admin/components/navigation/ResponsiveHeader.tsx b/apps/frontend/src/pages/Admin/components/navigation/ResponsiveHeader.tsx new file mode 100644 index 00000000..4fd494dc --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/navigation/ResponsiveHeader.tsx @@ -0,0 +1,35 @@ +import { Box, Heading, HStack, IconButton } from "@chakra-ui/react"; +import { MenuIcon } from "@/icons"; +import { ConnectButton } from "@/components/ui/ConnectButton"; +import { useI18nContext } from "@/i18n/i18n-react"; + +interface ResponsiveHeaderProps { + showMenuButton: boolean; + onMenuToggle: () => void; +} + +export function ResponsiveHeader({ showMenuButton, onMenuToggle }: ResponsiveHeaderProps) { + const { LL } = useI18nContext(); + + return ( + + + {showMenuButton && ( + } + variant="ghost" + size="md" + onClick={onMenuToggle} + /> + )} + + {LL.admin.title()} + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/navigation/ResponsiveNavigation.tsx b/apps/frontend/src/pages/Admin/components/navigation/ResponsiveNavigation.tsx new file mode 100644 index 00000000..eb66285e --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/navigation/ResponsiveNavigation.tsx @@ -0,0 +1,47 @@ +import { useMemo, useState } from "react"; +import { useResponsiveNavigation } from "../../hooks/useResponsiveNavigation"; +import { HorizontalLayout } from "./layouts/HorizontalLayout"; +import { MobileLayout } from "./layouts/MobileLayout"; + +interface ResponsiveNavigationProps { + contractsContent: React.ReactNode[]; + utilsContent: React.ReactNode[]; +} + +export function ResponsiveNavigation({ contractsContent, utilsContent }: ResponsiveNavigationProps) { + const { showMobileDrawer, drawerState } = useResponsiveNavigation(); + + const [mainTabIndex, setMainTabIndex] = useState(0); + const [contractsTabIndex, setContractsTabIndex] = useState(0); + const [utilsTabIndex, setUtilsTabIndex] = useState(0); + + const handleMainTabChange = (index: number) => { + setMainTabIndex(index); + }; + + const handleContractsTabChange = (index: number) => { + setContractsTabIndex(index); + }; + + const handleUtilsTabChange = (index: number) => { + setUtilsTabIndex(index); + }; + + const commonProps = useMemo( + () => ({ + mainTabIndex, + contractsTabIndex, + utilsTabIndex, + onMainTabChange: handleMainTabChange, + onContractsTabChange: handleContractsTabChange, + onUtilsTabChange: handleUtilsTabChange, + contractsContent, + utilsContent, + }), + [mainTabIndex, contractsTabIndex, utilsTabIndex, contractsContent, utilsContent], + ); + + if (showMobileDrawer) return ; + + return ; +} diff --git a/apps/frontend/src/pages/Admin/components/navigation/layouts/HorizontalLayout.tsx b/apps/frontend/src/pages/Admin/components/navigation/layouts/HorizontalLayout.tsx new file mode 100644 index 00000000..1c106039 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/navigation/layouts/HorizontalLayout.tsx @@ -0,0 +1,45 @@ +import { TabPanel } from "@chakra-ui/react"; +import { MainTabs, HorizontalTabs } from "../NavigationComponents"; +import { ResponsiveHeader } from "../ResponsiveHeader"; + +interface HorizontalLayoutProps { + mainTabIndex: number; + contractsTabIndex: number; + utilsTabIndex: number; + onMainTabChange: (index: number) => void; + onContractsTabChange: (index: number) => void; + onUtilsTabChange: (index: number) => void; + contractsContent: React.ReactNode[]; + utilsContent: React.ReactNode[]; +} + +export function HorizontalLayout({ + mainTabIndex, + contractsTabIndex, + utilsTabIndex, + onMainTabChange, + onContractsTabChange, + onUtilsTabChange, + contractsContent, + utilsContent, +}: HorizontalLayoutProps) { + return ( + <> + {}} /> + + + + + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/components/navigation/layouts/MobileLayout.tsx b/apps/frontend/src/pages/Admin/components/navigation/layouts/MobileLayout.tsx new file mode 100644 index 00000000..1fcf4a13 --- /dev/null +++ b/apps/frontend/src/pages/Admin/components/navigation/layouts/MobileLayout.tsx @@ -0,0 +1,54 @@ +import { MobileNavDrawer } from "../MobileNavDrawer"; +import { ResponsiveHeader } from "../ResponsiveHeader"; +import { MobileContent } from "../NavigationComponents"; + +interface MobileLayoutProps { + mainTabIndex: number; + contractsTabIndex: number; + utilsTabIndex: number; + onMainTabChange: (index: number) => void; + onContractsTabChange: (index: number) => void; + onUtilsTabChange: (index: number) => void; + contractsContent: React.ReactNode[]; + utilsContent: React.ReactNode[]; + drawerState: { + isOpen: boolean; + onClose: () => void; + onToggle: () => void; + }; +} + +export function MobileLayout({ + mainTabIndex, + contractsTabIndex, + utilsTabIndex, + onMainTabChange, + onContractsTabChange, + onUtilsTabChange, + contractsContent, + utilsContent, + drawerState, +}: MobileLayoutProps) { + return ( + <> + + + + + + + ); +} diff --git a/apps/frontend/src/pages/Admin/constants/contracts.ts b/apps/frontend/src/pages/Admin/constants/contracts.ts new file mode 100644 index 00000000..797ced2f --- /dev/null +++ b/apps/frontend/src/pages/Admin/constants/contracts.ts @@ -0,0 +1,34 @@ +export type ContractType = "vevote" | "nodeManagement" | "stargate"; + +export const CONTRACT_CONFIGS = { + vevote: { + roles: [ + "DEFAULT_ADMIN_ROLE", + "UPGRADER_ROLE", + "WHITELIST_ADMIN_ROLE", + "WHITELISTED_ROLE", + "EXECUTOR_ROLE", + "SETTINGS_MANAGER_ROLE", + "NODE_WEIGHT_MANAGER_ROLE", + ], + addressKey: "vevoteContractAddress" as const, + }, + nodeManagement: { + roles: [ + "DEFAULT_ADMIN_ROLE", + "UPGRADER_ROLE", + ], + addressKey: "nodeManagementContractAddress" as const, + }, + stargate: { + roles: [ + "DEFAULT_ADMIN_ROLE", + "UPGRADER_ROLE", + "PAUSER_ROLE", + "LEVEL_OPERATOR_ROLE", + "MANAGER_ROLE", + "WHITELISTER_ROLE", + ], + addressKey: "stargateNFTContractAddress" as const, + }, +} as const; \ No newline at end of file diff --git a/apps/frontend/src/pages/Admin/hooks/index.ts b/apps/frontend/src/pages/Admin/hooks/index.ts new file mode 100644 index 00000000..00e51823 --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/index.ts @@ -0,0 +1,4 @@ +export { useVeVoteInfo } from "./useVeVoteInfo"; +export { useUserNodeInfo, useNodeLevel, useIsNodeHolder } from "./useNodeManagement"; +export { useStargateStats, useStargateLevels, useTokensOwnedBy } from "./useStargateData"; +export { useResponsiveNavigation } from "./useResponsiveNavigation"; \ No newline at end of file diff --git a/apps/frontend/src/pages/Admin/hooks/useContractAddresses.ts b/apps/frontend/src/pages/Admin/hooks/useContractAddresses.ts new file mode 100644 index 00000000..4cce5eee --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/useContractAddresses.ts @@ -0,0 +1,16 @@ +import { useQuery } from "@tanstack/react-query"; +import { veVoteService } from "../services"; + +export const useContractAddresses = () => { + const { data, isLoading, error } = useQuery({ + queryKey: ["contractAddresses"], + queryFn: async () => await veVoteService.getContractAddress(), + staleTime: 5 * 60 * 1000, + }); + + return { + contractAddresses: data, + isLoading, + error, + }; +}; diff --git a/apps/frontend/src/pages/Admin/hooks/useLevelMultipliersInfo.ts b/apps/frontend/src/pages/Admin/hooks/useLevelMultipliersInfo.ts new file mode 100644 index 00000000..18fb9beb --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/useLevelMultipliersInfo.ts @@ -0,0 +1,10 @@ +import { useQuery } from "@tanstack/react-query"; +import { veVoteService } from "../services/VeVoteService"; + +export const useLevelMultipliersInfo = () => { + return useQuery({ + queryKey: ["levelMultipliers"], + queryFn: () => veVoteService.getLevelMultipliers(), + staleTime: 1000 * 60 * 5, + }); +}; diff --git a/apps/frontend/src/pages/Admin/hooks/useNodeManagement.ts b/apps/frontend/src/pages/Admin/hooks/useNodeManagement.ts new file mode 100644 index 00000000..8a291a04 --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/useNodeManagement.ts @@ -0,0 +1,29 @@ +import { useQuery } from "@tanstack/react-query"; +import { nodeManagementService } from "../services"; + +export function useUserNodeInfo(userAddress: string) { + return useQuery({ + queryKey: ["userNodeInfo", userAddress], + queryFn: () => nodeManagementService.getUserNodeInfo(userAddress), + enabled: !!userAddress.trim(), + staleTime: 2 * 60 * 1000, + }); +} + +export function useNodeLevel(nodeId: bigint) { + return useQuery({ + queryKey: ["nodeLevel", nodeId.toString()], + queryFn: () => nodeManagementService.getNodeLevel(nodeId), + enabled: !!nodeId, + staleTime: 10 * 60 * 1000, + }); +} + +export function useIsNodeHolder(userAddress: string) { + return useQuery({ + queryKey: ["isNodeHolder", userAddress], + queryFn: () => nodeManagementService.isNodeHolder(userAddress), + enabled: !!userAddress.trim(), + staleTime: 2 * 60 * 1000, + }); +} diff --git a/apps/frontend/src/pages/Admin/hooks/useResponsiveNavigation.ts b/apps/frontend/src/pages/Admin/hooks/useResponsiveNavigation.ts new file mode 100644 index 00000000..c97cfa95 --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/useResponsiveNavigation.ts @@ -0,0 +1,39 @@ +import { useBreakpointValue, useDisclosure } from "@chakra-ui/react"; +import { useMemo } from "react"; + +export function useResponsiveNavigation() { + const { isOpen, onOpen, onClose, onToggle } = useDisclosure(); + + const layout = useBreakpointValue( + { + base: "sm", + md: "md", + lg: "lg", + }, + { + fallback: "sm", + }, + ); + + const resultLayout = useMemo(() => { + const showVerticalTabs = layout === "lg"; + const showHorizontalTabs = layout === "md"; + const showMobileDrawer = layout === "sm"; + return { + layout, + showVerticalTabs, + showHorizontalTabs, + showMobileDrawer, + }; + }, [layout]); + + return { + ...resultLayout, + drawerState: { + isOpen, + onOpen, + onClose, + onToggle, + }, + }; +} diff --git a/apps/frontend/src/pages/Admin/hooks/useStargateData.ts b/apps/frontend/src/pages/Admin/hooks/useStargateData.ts new file mode 100644 index 00000000..2be4b53b --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/useStargateData.ts @@ -0,0 +1,28 @@ +import { useQuery } from "@tanstack/react-query"; +import { stargateService } from "../services"; + +export function useStargateStats() { + return useQuery({ + queryKey: ["stargateStats"], + queryFn: () => stargateService.getStargateStats(), + staleTime: 5 * 60 * 1000, + refetchInterval: 15 * 60 * 1000, + }); +} + +export function useStargateLevels() { + return useQuery({ + queryKey: ["stargateLevels"], + queryFn: () => stargateService.getLevels(), + staleTime: 10 * 60 * 1000, + }); +} + +export function useTokensOwnedBy(owner: string) { + return useQuery({ + queryKey: ["tokensOwnedBy", owner], + queryFn: () => stargateService.getTokensOwnedBy(owner), + enabled: !!owner.trim(), + staleTime: 2 * 60 * 1000, + }); +} diff --git a/apps/frontend/src/pages/Admin/hooks/useVeVoteInfo.ts b/apps/frontend/src/pages/Admin/hooks/useVeVoteInfo.ts new file mode 100644 index 00000000..8ee46a59 --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/useVeVoteInfo.ts @@ -0,0 +1,11 @@ +import { useQuery } from "@tanstack/react-query"; +import { veVoteService } from "../services"; + +export function useVeVoteInfo() { + return useQuery({ + queryKey: ["veVoteInfo"], + queryFn: async () => await veVoteService.getVeVoteInfo(), + staleTime: 5 * 60 * 1000, // 5 minutes + refetchInterval: 30 * 60 * 1000, // 30 minutes + }); +} diff --git a/apps/frontend/src/pages/Admin/hooks/useVotingPowerAtTimepoint.ts b/apps/frontend/src/pages/Admin/hooks/useVotingPowerAtTimepoint.ts new file mode 100644 index 00000000..d301ca3b --- /dev/null +++ b/apps/frontend/src/pages/Admin/hooks/useVotingPowerAtTimepoint.ts @@ -0,0 +1,45 @@ +import { useQuery } from "@tanstack/react-query"; +import { veVoteService } from "../services/VeVoteService"; +import { ZERO_ADDRESS } from "@vechain/sdk-core"; + +export interface VotingPowerAtTimepointResult { + nodePower: bigint; + validatorPower: bigint; + totalPower: bigint; +} + +interface UseVotingPowerAtTimepointProps { + address?: string; + timepoint?: number; + masterAddress?: string; +} + +export const useVotingPowerAtTimepoint = ({ address, timepoint, masterAddress }: UseVotingPowerAtTimepointProps) => { + return useQuery({ + queryKey: ["votingPowerAtTimepoint", address, timepoint, masterAddress], + queryFn: async (): Promise => { + if (!address || timepoint === undefined) { + return { + nodePower: BigInt(0), + validatorPower: BigInt(0), + totalPower: BigInt(0), + }; + } + + const [nodePower, validatorPower] = await Promise.all([ + veVoteService.getVotingPowerAtTimepoint(address, timepoint, ZERO_ADDRESS), + masterAddress + ? veVoteService.getVotingPowerAtTimepoint(address, timepoint, masterAddress) + : Promise.resolve(BigInt(0)), + ]); + + return { + nodePower, + validatorPower, + totalPower: nodePower + validatorPower, + }; + }, + enabled: !!address && timepoint !== undefined, + staleTime: 30000, + }); +}; diff --git a/apps/frontend/src/pages/Admin/services/NodeManagementService.ts b/apps/frontend/src/pages/Admin/services/NodeManagementService.ts new file mode 100644 index 00000000..3e77801d --- /dev/null +++ b/apps/frontend/src/pages/Admin/services/NodeManagementService.ts @@ -0,0 +1,111 @@ +import { NodeManagement__factory } from "@vechain/vevote-contracts/typechain-types"; +import { getConfig } from "@repo/config"; +import { executeCall, executeMultipleClauses } from "../../../utils/contract"; + +export interface NodeStats { + totalNodes: number; + delegatedNodes: number; + levelDistribution: { [level: number]: number }; +} + +export interface UserNodeInfo { + ownedNodes: bigint[]; + managedNodes: bigint[]; + isNodeHolder: boolean; + isNodeDelegator: boolean; +} + +export class NodeManagementService { + private readonly contractAddress: string; + private readonly contractInterface: ReturnType; + + constructor() { + const config = getConfig(import.meta.env.VITE_APP_ENV); + this.contractAddress = config.nodeManagementContractAddress; + this.contractInterface = NodeManagement__factory.createInterface(); + } + + async getUserNodeInfo(userAddress: string): Promise { + const methodsWithArgs = [ + { method: "getDirectNodesOwnership" as const, args: [userAddress] }, + { method: "getNodeIds" as const, args: [userAddress] }, + { method: "isNodeHolder" as const, args: [userAddress] }, + { method: "isNodeDelegator" as const, args: [userAddress] }, + ]; + + const results = await executeMultipleClauses({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + methodsWithArgs, + }); + + return { + ownedNodes: + results[0]?.success && Array.isArray(results[0].result.plain) + ? results[0].result.plain.map((id: unknown) => BigInt(String(id))) + : [], + managedNodes: + results[1]?.success && Array.isArray(results[1].result.plain) + ? results[1].result.plain.map((id: unknown) => BigInt(String(id))) + : [], + isNodeHolder: Boolean(results[2]?.success ? results[2].result.plain : false), + isNodeDelegator: Boolean(results[3]?.success ? results[3].result.plain : false), + }; + } + + async getNodeLevel(nodeId: bigint): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "getNodeLevel", + args: [nodeId], + }); + return result?.success ? Number(result.result.plain) : 0; + } + + async isNodeHolder(userAddress: string): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "isNodeHolder", + args: [userAddress], + }); + return Boolean(result?.success ? result.result.plain : false); + } + + async isNodeDelegator(userAddress: string): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "isNodeDelegator", + args: [userAddress], + }); + return Boolean(result?.success ? result.result.plain : false); + } + + async getDirectNodesOwnership(userAddress: string): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "getDirectNodesOwnership", + args: [userAddress], + }); + return result?.success && Array.isArray(result.result.plain) + ? result.result.plain.map((id: unknown) => BigInt(String(id))) + : []; + } + + async getNodeIds(userAddress: string): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "getNodeIds", + args: [userAddress], + }); + return result?.success && Array.isArray(result.result.plain) + ? result.result.plain.map((id: unknown) => BigInt(String(id))) + : []; + } +} + +export const nodeManagementService = new NodeManagementService(); diff --git a/apps/frontend/src/pages/Admin/services/StargateService.ts b/apps/frontend/src/pages/Admin/services/StargateService.ts new file mode 100644 index 00000000..a897fa7f --- /dev/null +++ b/apps/frontend/src/pages/Admin/services/StargateService.ts @@ -0,0 +1,122 @@ +import { StargateNFT__factory } from "@vechain/vevote-contracts/typechain-types"; +import { getConfig } from "@repo/config"; +import { executeCall, executeMultipleClauses } from "../../../utils/contract"; + +export interface StargateLevel { + name: string; + maturityBlocks: bigint; + vetAmountRequiredToStake: bigint; +} + +export interface StargateLevelSupply { + circulating: bigint; + cap: number; +} + +export interface StargateToken { + tokenId: bigint; + owner: string; + levelId: number; + mintTimestamp: bigint; +} + +export interface StargateStats { + totalSupply: bigint; + levelIds: number[]; + levels: StargateLevel[]; +} + +export class StargateService { + private readonly contractAddress: string; + private readonly contractInterface: ReturnType; + + constructor() { + const config = getConfig(import.meta.env.VITE_APP_ENV); + this.contractAddress = config.stargateNFTContractAddress; + this.contractInterface = StargateNFT__factory.createInterface(); + } + + async getStargateStats(): Promise { + const levelIds = await this.getLevelIds(); + + const methodsWithArgs = [ + { method: "totalSupply" as const, args: [] }, + { method: "getLevels" as const, args: [] }, + ]; + + const results = await executeMultipleClauses({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + methodsWithArgs, + }); + + const totalSupply = BigInt(results[0]?.success ? String(results[0].result.plain) : "0"); + const levels = (results[1]?.success ? results[1].result.plain : []) as StargateLevel[]; + + return { + totalSupply, + levelIds, + levels, + }; + } + + async getLevelIds(): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "getLevelIds", + args: [], + }); + return result?.success && Array.isArray(result.result.plain) + ? result.result.plain.map((id: unknown) => Number(id)) + : []; + } + + async getLevels(): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "getLevels", + args: [], + }); + return result?.success && Array.isArray(result.result.plain) ? result.result.plain : []; + } + + async getLevelSupply(levelId: number): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "getLevelSupply", + args: [levelId], + }); + if (result?.success && Array.isArray(result.result.plain)) { + return { + circulating: BigInt(String(result.result.plain[0] || "0")), + cap: Number(result.result.plain[1] || 0), + }; + } + return { circulating: BigInt("0"), cap: 0 }; + } + + async getTotalSupply(): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "totalSupply", + args: [], + }); + return BigInt(result?.success ? String(result.result.plain) : "0"); + } + + async getTokensOwnedBy(owner: string): Promise { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "tokensOwnedBy", + args: [owner], + }); + return result?.success && Array.isArray(result.result.plain) ? result.result.plain : []; + } +} + +export const stargateService = new StargateService(); diff --git a/apps/frontend/src/pages/Admin/services/VeVoteService.ts b/apps/frontend/src/pages/Admin/services/VeVoteService.ts new file mode 100644 index 00000000..b84b3c73 --- /dev/null +++ b/apps/frontend/src/pages/Admin/services/VeVoteService.ts @@ -0,0 +1,265 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { VeVote__factory } from "@vechain/vevote-contracts"; +import { getConfig } from "@repo/config"; +import { executeCall, executeMultipleClauses } from "../../../utils/contract"; +import { ZERO_ADDRESS } from "@vechain/sdk-core"; +import { getAllEventLogs, ThorClient } from "@vechain/vechain-kit"; +import { VALIDATOR_STAKED_VET_REQUIREMENT } from "@/constants"; +import { StargateLevelRow } from "../components/Contracts/components/StargateLevelDetails"; + +export interface VeVoteInfo { + quorumNumerator: bigint; + quorumDenominator: bigint; + minVotingDelay: number; + minVotingDuration: number; + maxVotingDuration: number; + minStakedAmount: bigint; + version: bigint; +} + +export interface ContractAddresses { + vevoteContractAddress: string; + nodeManagementAddress: string; + stargateAddress: string; +} + +type VevoteContractInterface = ReturnType; + +export interface VotingMultipliers { + [levelId: number]: bigint; +} + +export interface RoleUserInfo { + address: string; + grantedAt: Date; + transactionId: string; + isCurrentlyGranted: boolean; + lastActionAt: Date; +} + +export class VeVoteService { + private readonly contractAddress: string; + private readonly contractInterface: VevoteContractInterface; + private readonly nodeUrl: string; + + constructor() { + const config = getConfig(import.meta.env.VITE_APP_ENV); + this.contractAddress = config.vevoteContractAddress; + this.contractInterface = VeVote__factory.createInterface(); + this.nodeUrl = config.nodeUrl; + } + + async getVeVoteInfo(): Promise { + const methodsWithArgs = [ + { method: "quorumDenominator" as const, args: [] }, + { method: "getMinVotingDelay" as const, args: [] }, + { method: "getMinVotingDuration" as const, args: [] }, + { method: "getMaxVotingDuration" as const, args: [] }, + { method: "getMinStakedAmount" as const, args: [] }, + { method: "version" as const, args: [] }, + ]; + + const results = await executeMultipleClauses({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + methodsWithArgs, + }); + + const quorumNumerator = await this.getQuorumNumerator(); + + return { + quorumNumerator, + quorumDenominator: BigInt(results[0]?.success ? String(results[0].result.plain) : "0"), + minVotingDelay: Number(results[1]?.success ? results[1].result.plain : 0), + minVotingDuration: Number(results[2]?.success ? results[2].result.plain : 0), + maxVotingDuration: Number(results[3]?.success ? results[3].result.plain : 0), + minStakedAmount: BigInt(results[4]?.success ? String(results[4].result.plain) : "0"), + version: BigInt(results[5]?.success ? String(results[5].result.plain) : "0"), + }; + } + + async getContractAddress(): Promise { + const methodsWithArgs = [ + { method: "getNodeManagementContract" as const, args: [] }, + { method: "getStargateNFTContract" as const, args: [] }, + ]; + + try { + const results = await executeMultipleClauses({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + methodsWithArgs, + }); + + return { + vevoteContractAddress: this.contractAddress, + nodeManagementAddress: results[0]?.success ? String(results[0].result.plain) : "", + stargateAddress: results[1]?.success ? String(results[1].result.plain) : "", + }; + } catch (error) { + console.error("Error fetching contract addresses:", error); + throw error; + } + } + + async getQuorumNumerator(): Promise { + try { + const result = await executeCall({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + method: "quorumNumerator()" as const, + args: [], + }); + return result?.success ? BigInt(result.result.plain as string) : BigInt(0); + } catch (error) { + console.error("Error fetching quorum numerator:", error); + return BigInt(0); + } + } + + async getLevelMultipliers(): Promise { + const DEFAULT_MULTIPLIERS = [200, 100, 100, 100, 150, 150, 150, 150, 100, 100, 100]; + const methodsWithArgs = Array.from({ length: 11 }, (_, index) => ({ + method: "levelIdMultiplier" as const, + args: [index], + })); + + try { + const results = await executeMultipleClauses({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + methodsWithArgs, + }); + + return results.map(result => (result?.success ? Number(result.result.plain) : 100)) || DEFAULT_MULTIPLIERS; + } catch (error) { + console.error("Error fetching level multipliers:", error); + return DEFAULT_MULTIPLIERS; + } + } + + async getVotingPowerAtTimepoint(address: string, timepoint: number, masterAddress?: string): Promise { + try { + const result = await executeMultipleClauses({ + contractAddress: this.contractAddress, + contractInterface: this.contractInterface, + methodsWithArgs: [ + { + method: "getVoteWeightAtTimepoint" as const, + args: [address, timepoint, masterAddress || ZERO_ADDRESS], + }, + ], + }); + + return result[0]?.success ? BigInt(result[0].result.plain as string) : BigInt(0); + } catch (error) { + console.error("Error fetching voting power at timepoint:", error); + return BigInt(0); + } + } + + getValidatorInfo(): StargateLevelRow { + return { + name: "Validator", + vetAmountRequiredToStake: VALIDATOR_STAKED_VET_REQUIREMENT, + levelId: 0, + maturityBlocks: BigInt(0), + }; + } + + async getRoleUsers(thor: ThorClient, roleHash: string): Promise { + if (!thor) { + return []; + } + + try { + const roleGrantedAbi = thor.contracts.load(this.contractAddress, VeVote__factory.abi).getEventAbi("RoleGranted"); + const roleRevokedAbi = thor.contracts.load(this.contractAddress, VeVote__factory.abi).getEventAbi("RoleRevoked"); + + const filterCriteria = [ + { + criteria: { + address: this.contractAddress, + topic0: roleGrantedAbi.signatureHash, + topic1: roleHash, + }, + eventAbi: roleGrantedAbi, + }, + { + criteria: { + address: this.contractAddress, + topic0: roleRevokedAbi.signatureHash, + topic1: roleHash, + }, + eventAbi: roleRevokedAbi, + }, + ]; + + const allEvents = await getAllEventLogs({ thor, nodeUrl: this.nodeUrl, filterCriteria }); + + const userEventsMap = new Map< + string, + Array<{ + type: "granted" | "revoked"; + timestamp: number; + blockNumber: number; + transactionId: string; + grantedAt?: Date; + }> + >(); + + allEvents.forEach(event => { + if (event.decodedData) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_role, account, _sender] = event.decodedData as [string, string, string]; + + if (!userEventsMap.has(account)) { + userEventsMap.set(account, []); + } + + const eventType = event.topics[0] === roleGrantedAbi.signatureHash ? "granted" : "revoked"; + + userEventsMap.get(account)!.push({ + type: eventType, + timestamp: event.meta.blockTimestamp, + blockNumber: event.meta.blockNumber, + transactionId: event.meta.txID, + grantedAt: eventType === "granted" ? new Date(event.meta.blockTimestamp * 1000) : undefined, + }); + } + }); + + const currentUsers: RoleUserInfo[] = []; + + userEventsMap.forEach((events, address) => { + events.sort((a, b) => { + if (a.blockNumber !== b.blockNumber) { + return a.blockNumber - b.blockNumber; + } + return a.timestamp - b.timestamp; + }); + + const lastEvent = events[events.length - 1]; + + if (lastEvent.type === "granted") { + const currentGrantEvent = lastEvent; + + currentUsers.push({ + address, + grantedAt: new Date(currentGrantEvent.timestamp * 1000), + transactionId: currentGrantEvent.transactionId, + isCurrentlyGranted: true, + lastActionAt: new Date(lastEvent.timestamp * 1000), + }); + } + }); + + return currentUsers.sort((a, b) => b.lastActionAt.getTime() - a.lastActionAt.getTime()); + } catch (error) { + console.error("Error fetching role users:", error); + return []; + } + } +} + +export const veVoteService = new VeVoteService(); diff --git a/apps/frontend/src/pages/Admin/services/index.ts b/apps/frontend/src/pages/Admin/services/index.ts new file mode 100644 index 00000000..4e4b5fd6 --- /dev/null +++ b/apps/frontend/src/pages/Admin/services/index.ts @@ -0,0 +1,15 @@ +export { VeVoteService, veVoteService, type VeVoteInfo } from "./VeVoteService"; +export { + NodeManagementService, + nodeManagementService, + type UserNodeInfo, + type NodeStats, +} from "./NodeManagementService"; +export { + StargateService, + stargateService, + type StargateLevel, + type StargateLevelSupply, + type StargateToken, + type StargateStats, +} from "./StargateService"; diff --git a/apps/frontend/src/schema/adminSchema.ts b/apps/frontend/src/schema/adminSchema.ts new file mode 100644 index 00000000..6c448ee6 --- /dev/null +++ b/apps/frontend/src/schema/adminSchema.ts @@ -0,0 +1,42 @@ +import { z } from "zod"; +import { addressSchema, requiredString } from "@/utils/zod"; + +export const userManagementSchema = z.object({ + userAddress: addressSchema, + selectedRole: requiredString, +}); + +export type UserManagementSchema = z.infer; + +export const nodeInfoSchema = z.object({ + userAddress: addressSchema, +}); + +export type NodeInfoSchema = z.infer; + +export const governanceSettingsSchema = z.object({ + quorumNumerator: z.literal("").or(z.coerce.number().min(1).max(100)), + minVotingDelay: z.literal("").or(z.coerce.number().min(1).max(259200)), + minVotingDuration: z.literal("").or(z.coerce.number().min(1).max(259200)), + maxVotingDuration: z.literal("").or(z.coerce.number().min(1).max(259200)), + minStakedVetAmount: z.literal("").or(z.coerce.number().min(0.001).max(1000000)), +}); + +export type GovernanceSettingsSchema = z.infer; + +export const votingPowerQuerySchema = z.object({ + address: addressSchema, + timepoint: z.coerce.number().min(0), + masterAddress: z + .string() + .optional() + .refine(val => !val || /^0x[a-fA-F0-9]{40}$/.test(val)), +}); + +export type VotingPowerQuerySchema = z.infer; + +export const levelMultipliersSchema = z.object({ + multipliers: z.array(z.coerce.number().min(0).max(1000)).length(11), +}); + +export type LevelMultipliersSchema = z.infer; diff --git a/apps/frontend/src/types/routes.ts b/apps/frontend/src/types/routes.ts index 2f83aebb..f801daef 100644 --- a/apps/frontend/src/types/routes.ts +++ b/apps/frontend/src/types/routes.ts @@ -2,4 +2,5 @@ export enum Routes { HOME = "/", PROPOSAL = "/proposal", CREATE_PROPOSAL = "/create-proposal", + ADMIN = "/admin", } diff --git a/apps/frontend/src/utils/proposals/helpers.ts b/apps/frontend/src/utils/proposals/helpers.ts index 0b703df8..0b2a9c10 100644 --- a/apps/frontend/src/utils/proposals/helpers.ts +++ b/apps/frontend/src/utils/proposals/helpers.ts @@ -272,3 +272,7 @@ export const parseHistoricalProposals = async ( }; }); }; + +export const formatVotingPower = (power: bigint): string => { + return (Number(power) / 100).toString(); +}; diff --git a/apps/frontend/src/utils/zod.ts b/apps/frontend/src/utils/zod.ts index c949b3b1..4ef7ef31 100644 --- a/apps/frontend/src/utils/zod.ts +++ b/apps/frontend/src/utils/zod.ts @@ -61,3 +61,11 @@ export const zodFile = z.object({ }); export type ZodFile = z.infer; + +export const isValidAddress = (address: string): boolean => { + return /^0x[a-fA-F0-9]{40}$/.test(address); +}; + +export const addressSchema = z.string().trim().min(1, { message: LL.field_errors.required() }).refine(isValidAddress, { + message: LL.field_errors.invalid_address(), +});