From 828c89c7d837762d4e2c20e904732f02abfcd4b0 Mon Sep 17 00:00:00 2001 From: Thom Ivy Date: Thu, 21 Mar 2024 15:27:06 -0500 Subject: [PATCH] Refactored the Services section, added a comprehensive rewards page with detailed logic and latex, began but hid an incomplete react component for estimating restaking rewards --- .../RestakingRewardsCalculator.module.css | 121 +++++++++++++ components/RestakingRewardsCalculator.tsx | 162 ++++++++++++++++++ components/RoleInput.tsx | 45 +++++ package.json | 1 + pages/docs/_meta.json | 6 - pages/docs/services/_meta.json | 8 +- pages/docs/services/restaking-reward-calc.mdx | 5 + pages/docs/services/restaking-rewards.mdx | 134 +++++++++++++++ pages/docs/services/service-provider.mdx | 39 ----- pages/docs/services/services.mdx | 32 ---- styles.css | 4 - yarn.lock | 5 + 12 files changed, 479 insertions(+), 83 deletions(-) create mode 100644 components/RestakingRewardsCalculator.module.css create mode 100644 components/RestakingRewardsCalculator.tsx create mode 100644 components/RoleInput.tsx create mode 100644 pages/docs/services/restaking-reward-calc.mdx create mode 100644 pages/docs/services/restaking-rewards.mdx diff --git a/components/RestakingRewardsCalculator.module.css b/components/RestakingRewardsCalculator.module.css new file mode 100644 index 00000000..f5bc812b --- /dev/null +++ b/components/RestakingRewardsCalculator.module.css @@ -0,0 +1,121 @@ +/* RestakingRewardsCalculator.module.css */ +.addButton { + background-color: #667eea; /* bg-indigo-600 */ + color: white; + padding: 8px 16px; + margin-top: 8px; + border: none; + cursor: pointer; + border-radius: 0.375rem; /* rounded-md */ + display: inline-block; + font-size: 0.875rem; /* text-sm */ + font-weight: 500; /* font-medium */ + } + + .addButton:hover { + background-color: #5a67d8; /* hover:bg-indigo-700 */ + } + + .removeButton { + background-color: #ea6666; /* bg-indigo-600 */ + color: white; + padding: 8px 16px; + margin-top: 8px; + border: none; + cursor: pointer; + border-radius: 0.375rem; /* rounded-md */ + display: inline-block; + font-size: 0.875rem; /* text-sm */ + font-weight: 500; /* font-medium */ + } + + .removeButton:hover { + background-color: #d85a5a; /* hover:bg-indigo-700 */ + } + + + .calcContainer { + margin: auto; + display: flex; + flex-wrap: wrap; /* Allows items to wrap to the next line */ + gap: 20px; /* Adjust the gap between items as needed */ + padding: 20px; + background-color: #4000e4; + } + + .calcContainer > * { + flex: 1 1 calc(50% - 20px); /* Each child takes up half the container width minus the gap */ + } + +.generalDetailsContainer { + max-width:30%; + background-color: #000e8e; + padding: 13px; +} + +.roleInputContainer { + background-color: #280569; + padding-left: 13px; + padding-right: 13px; +} + + .space-y-4 > * + * { + margin-top: 1rem; + } + + .selectInput, + .numberInput, + .addButton { + width: 100%; + padding: 0.5rem; + margin-top: 0.5rem; + border: 1px solid #ddd; + border-radius: 0.375rem; /* rounded-md */ + font-size: 1rem; + } + + .selectInput:focus, + .numberInput:focus { + outline: none; + border-color: #4F46E5; /* Indigo-500 */ + box-shadow: 0 0 0 1px #4F46E5; + } + + + .label { + display: block; + font-size: 0.875rem; /* text-sm */ + font-weight: medium; + color: #ffffff; /* Gray-700 */ + } + + .title { + font-size: 1.2rem; /* text-xs */ + color: #ffffff; /* Gray-500 */ + } + + .note { + font-size: 0.75rem; /* text-xs */ + color: #ffffff; /* Gray-500 */ + } + + .result { + padding: 1rem; + margin-top: 1rem; + background-color: #f0f4f8; /* A very light shade of blue to distinguish the result area */ + border-radius: 0.375rem; /* rounded-md */ + border: 1px solid #e2e8f0; /* A light gray border for subtle definition */ + } + + .resultTitle { + font-size: 1.125rem; /* text-lg */ + font-weight: 600; /* Semi-bold for the title */ + color: #374151; /* Gray-700 for contrast and readability */ + margin-bottom: 0.5rem; /* Space between title and value */ + } + + .resultValue { + font-size: 1.875rem; /* text-3xl */ + font-weight: 700; /* Bold for the value to stand out */ + color: #1F2937; /* Gray-800 for maximum contrast and emphasis */ + } \ No newline at end of file diff --git a/components/RestakingRewardsCalculator.tsx b/components/RestakingRewardsCalculator.tsx new file mode 100644 index 00000000..37a16462 --- /dev/null +++ b/components/RestakingRewardsCalculator.tsx @@ -0,0 +1,162 @@ +import React, { useState, useEffect } from "react"; +import RoleInput from "./RoleInput"; +import styles from "./RestakingRewardsCalculator.module.css"; + +interface Role { + name: string; + restakeAmount: number; + jobDistribution: number; + weight: number; +} + +const RestakingRewardsCalculator: React.FC = () => { + const [restakingProfile, setRestakingProfile] = useState("independent"); + const [totalRestake, setTotalRestake] = useState(1000); + const [roles, setRoles] = useState([ + { name: "", restakeAmount: 0, jobDistribution: 0, weight: 0 }, + ]); + const [stakingRate, setStakingRate] = useState(0.25); + const [result, setResult] = useState(null); + + const calculateReward = () => { + const totalSupply = 100000000; + const idealStakingRate = 0.25; + const minInflation = 0.001; + const idealInflation = 0.02; + const decayRate = 0.05; + + const inflationRate = + stakingRate <= idealStakingRate + ? minInflation + + (idealInflation - minInflation) * (stakingRate / idealStakingRate) + : minInflation + + (idealInflation - minInflation) * + Math.pow(2, (idealStakingRate - stakingRate) / decayRate); + + const payout = inflationRate * totalSupply; + + const rewardSum = roles.reduce( + (sum, role) => sum + role.weight * role.jobDistribution, + 0 + ); + const restakeFraction = + restakingProfile === "independent" + ? roles.reduce((sum, role) => sum + role.restakeAmount, 0) / + totalRestake + : 1; + + const reward = + payout * + (rewardSum + + (1 - roles.reduce((sum, role) => sum + role.weight, 0)) * + restakeFraction); + + setResult(reward); + }; + + // Recalculate reward whenever relevant states change + useEffect(() => { + calculateReward(); + }, [restakingProfile, totalRestake, roles, stakingRate]); + + const addRole = () => + setRoles([ + ...roles, + { name: "", restakeAmount: 0, jobDistribution: 0, weight: 0 }, + ]); + + const removeRole = (index: number) => + setRoles(roles.filter((_, i) => i !== index)); + + const updateRole = (index: number, field: keyof Role, value: string) => { + const updatedRoles = roles.map((role, i) => + i === index + ? { ...role, [field]: field === "name" ? value : Number(value) } + : role + ); + setRoles(updatedRoles); + }; + + return ( +
+ {/* Restaking Profile */} +
+
+ + +
+ + {/* Total Restake Amount */} +
+ + setTotalRestake(Number(e.target.value))} + className={styles.numberInput} + placeholder="Enter total restake amount" + min="0" + /> +
+ + {/* Current Staking Rate */} +
+ + setStakingRate(Number(e.target.value))} + className={styles.numberInput} + placeholder="Enter current staking rate" + step="0.01" + min="0" + max="1" + /> +
+
+
+ {/* Dynamic Roles Inputs */} + {roles.map((role, index) => ( + + ))} + +
+ {/* Display the calculation result */} + {result !== null && ( +
+

Restaking Reward:

+

{result.toFixed(2)} TNT

+
+ )} +
+ ); +}; + +export default RestakingRewardsCalculator; diff --git a/components/RoleInput.tsx b/components/RoleInput.tsx new file mode 100644 index 00000000..202a902c --- /dev/null +++ b/components/RoleInput.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import styles from "./RestakingRewardsCalculator.module.css"; + +interface Role { + name: string; + restakeAmount: number; + jobDistribution: number; + weight: number; +} + +interface RoleInputProps { + role: Role; + index: number; + updateRole: (index: number, field: keyof Role, value: string) => void; + removeRole: (index: number) => void; +} + +const RoleInput: React.FC = ({ + role, + index, + updateRole, + removeRole, +}) => { + return ( +
+ updateRole(index, "name", e.target.value)} + className={styles.input} + placeholder="Role name" + /> + {/* Repeat for restakeAmount, jobDistribution, and weight with appropriate changes */} + +
+ ); +}; + +export default RoleInput; diff --git a/package.json b/package.json index ecb5e065..773e8b65 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "nextra-theme-docs": "2.12.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.51.1", "react-hot-toast": "2.4.0", "react-icons": "^4.12.0", "react-use": "^17.4.0", diff --git a/pages/docs/_meta.json b/pages/docs/_meta.json index cd934dcf..a3c48085 100644 --- a/pages/docs/_meta.json +++ b/pages/docs/_meta.json @@ -23,9 +23,6 @@ "title": "Services", "type": "doc" }, - "----": { - "type": "separator" - }, "network-information-configuration": { "title": "Network Configuration", "type": "doc" @@ -46,9 +43,6 @@ "title": "Govern", "type": "doc" }, - "---": { - "type": "separator" - }, "community": { "title": "Community", "type": "doc" diff --git a/pages/docs/services/_meta.json b/pages/docs/services/_meta.json index f41e881d..7340faf5 100644 --- a/pages/docs/services/_meta.json +++ b/pages/docs/services/_meta.json @@ -1,4 +1,8 @@ { - "services": "MPC and ZK Services", - "service-provider": "Restaking and Jobs" + "services": "Intro to MPC and ZK Services", + "service-provider": "Restaking Jobs", + "restaking-rewards": "Restaking Rewards", + "restaking-reward-calc": { + "display": "hidden" + } } diff --git a/pages/docs/services/restaking-reward-calc.mdx b/pages/docs/services/restaking-reward-calc.mdx new file mode 100644 index 00000000..454901f4 --- /dev/null +++ b/pages/docs/services/restaking-reward-calc.mdx @@ -0,0 +1,5 @@ +import RestakingRewardsCalculator from 'components/RestakingRewardsCalculator.tsx'; + +# Restaking Rewards Estimator + + \ No newline at end of file diff --git a/pages/docs/services/restaking-rewards.mdx b/pages/docs/services/restaking-rewards.mdx new file mode 100644 index 00000000..84c98b12 --- /dev/null +++ b/pages/docs/services/restaking-rewards.mdx @@ -0,0 +1,134 @@ +# Restaking Rewards + +## Introduction + +In addition to staking rewards, the Tangle Network also offers a restaking system designed to encourage validators to execute multi-party computation (MPC) service roles. This documentation provides a detailed overview of the restaking process, reward mechanisms, and the benefits for validators and nominators participating in the Tangle blockchain network. + +### Eligibility + +- Validators on the Tangle blockchain can establish a restaking profile by allocating 50% of their locked and nominated stake towards restaking. +- Nominators associated with these validators receive proportional rewards, enhancing the benefits of participating in the restaking system. + +### Reward Subtypes + +The rewards for restakers adjust proportionally based on several factors, including the amount of tokens restaked, the number of jobs completed, and the diversity of roles engaged in restaking. + +The reward distribution mechanism for restakers is bifurcated into two segments: + +#### Active Participation Rewards +The main form of reward is the income from providing services. These are fees users pay to have restaked validators perform MPC and ZK services. Rewards are based on the validator's participation in and completion of total job opportunities, benefiting the most active and reliable restakers. + - The total jobs completed (TJC) by all active validators are calculated. + - Each validator's share (VS) of the rewards is determined by the proportion of their completed jobs relative to the TJC. + - The validator reward (VR) is computed as: VR = VS × Total Rewards + +#### Continuous Restaking Rewards: +In addition, To motivate validators to opt into MPC service roles, Tangle rewards them with additional inflationary rewards on top of their standard validation rewards, merely for restaking their tokens.Rewards are allocated in proportion to the overall restaking distribution, maintaining the incentive for continuous restaking. The reward rate for this is programatically limited to a range of 0.1% and 2%, in a similar style to the core staking rewards system of Substrate blockchains. + - The total restake (TR) in the system is calculated by summing all validators' restaked amounts. + - The restake-to-stake ratio (RSR) is computed as: RSR = TR / Total Stake in the Era + - The missing restake ratio (MRR) is calculated as: MRR = Maximum Restake Ratio - RSR + - The adjusted total rewards (ATR) are computed based on the MRR to ensure proper reward distribution. + - Each validator's reward share (RS) is determined by their restaked amount relative to the TR. + - The final reward for each validator is calculated as: Reward = RS × ATR + + +### Example Reward Calculation + +Let's consider an example in era 100, with 20 restakers and a total role reward of 1000 TNT: + +- **Active Validator Rewards**: + - 10 restakers have completed 5 jobs each. + - 50% of the 1000 TNT (500 TNT) is equally divided among the 10 active restakers. + - If one restaker had completed more jobs than the others, they would receive a larger share of the rewards. + +- **Rewards by Restake**: + - The remaining 50% of the 1000 TNT (500 TNT) is distributed among all restakers based on their restaked amount. + - If a restaker has restaked 100 TNT out of a total restake of 1000 TNT, they are eligible for 10% of the rewards. + - The rewards are adjusted based on the total restake relative to the total stake in the system. + + +## Calculation Logic for Restaking Reward + +### Rewards and the Restaking Profile + +To begin restaking, a validator must create a restaking profile. A restaking profile involves allocating a portion of the validator's staked tokens to specific roles. There are two types of restaking profiles: + +**Independent Restaking Profile:** The restake amounts are allocated independently to each selected role, such that: + +$\sum_{r \in p} \text{role\_restake}(p, r) = \text{total\_restake}(p)$ + +**Shared Restaking Profile:** The restake amounts are allocated collectively to all selected roles, such that: + +$\text{role\_restake}(p, r) = \text{total\_restake}(p)$ + +A validator can update their restaking profile to switch between profile types, adjust the restake amounts allocated to various roles, or add/remove roles from their profile. The specific constraints for updating a restaking profile depend on whether the profile is active or not. + +### Active Participation Restaking Rewards + +Restaking rewards are calculated based on the validator's participation in various roles and their restaking fraction. + +The reward function for a validator $v$ over a time interval $T$ is defined as: + +$R(v, T) = \texttt{payout}(T) \times \left(\sum_{r} \alpha_r \times \texttt{dist}(v, r, T) + \left(1 - \sum_{r} \alpha_r\right) \times f_\texttt{restake}(v)\right)$ + +where: + +| Expression | Description | +| ------------------------ | --------------------------------------------------------------------------------------------- | +| $\texttt{payout}(T)$ | The total amount of tokens to be distributed as restaking rewards over the time interval $T$. | +| $\alpha_r$ | The weight assigned to role $r$ in the reward distribution ($\sum_{r} \alpha_r \leq 1$). | +| $\texttt{dist}(v, r, T)$ | The fraction of jobs with role $r$ completed by validator $v$ over the time interval $T$. | +| $f_\texttt{restake}(v)$ | The fraction of total restake by validator $v$. | + +The total payout for a given time interval $T$ is calculated as: + +$\texttt{payout}(T) = I_{\texttt{NPoS}}(x) \times \texttt{total\_supply}$ + +where: + +| Expression | Description | +| ---------------------- | ----------------------------------------------------------------------------------------------- | +| $I_{\texttt{NPoS}}(x)$ | The annual inflation rate for restaking rewards, which depends on the current staking rate $x$. | +| $\text{total\_supply}$ | The total token supply (100 million TNT). | + +### Continous Restaking Rewards (Annual Restaking Inflation Rate) + +The annual inflation rate for restaking rewards is dynamic and designed to incentivize a target staking rate of 25% of the total token supply. The inflation rate is calculated using the following piecewise function: + +$I_\texttt{NPoS}(x) = \begin{cases} +I_0 + (I_\texttt{ideal} - I_0) \times \frac{x}{\chi_\texttt{ideal}}, & \text{for } 0 < x \leq \chi_\texttt{ideal} \ +I_0 + (I_\texttt{ideal} - I_0) \times 2^{(\chi_\texttt{ideal} - x) / d}, & \text{for } \chi_\texttt{ideal} < x \leq 1 +\end{cases}$ + +where: + +| Expression | Description | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| $x$ | The staking rate (ratio of the total amount of tokens staked to the total token supply) | +| $\chi_{\text{ideal}}$ | The ideal staking rate (25% or 0.25) | +| $I_0$ | The minimum inflation rate when the staking rate is close to zero (0.1% or 0.001) | +| $I_{\text{ideal}}$ | The maximum inflation rate when the staking rate equals the ideal staking rate (2% or 0.02) | +| $d$ | The decay rate, which determines how quickly the inflation rate decreases when the staking rate exceeds the ideal rate (suggested value: 0.05) | + +### Calculating Total Restaking Rewards: Combined participation and continuous restaking + +To estimate a validator's restaking rewards, the following inputs are required: + +1. The validator's restaking profile (independent or shared) +2. The total amount of tokens restaked by the validator $\texttt{total\_restake}$ +3. The amount of tokens restaked by the validator for each role $r$ $\texttt{role\_restake}(r)$ +4. The fraction of jobs with each role $r$ completed by the validator $\texttt{dist}(v, r, T)$ +5. The total token supply $\texttt{total\_supply}$ +6. The current staking rate $x$ +7. The weights assigned to each role $r$ $\alpha_r$ + +With these inputs, the restaking rewards can be calculated using the following steps: + +1. Calculate the annual inflation rate for restaking rewards $I_\texttt{NPoS}(x)$ using the piecewise function based on the current staking rate ($x$) + +2. Calculate the total payout for the time interval $T$ $\texttt{payout}(T)$ using the formula: + +$I_\texttt{NPoS}(x) \times \texttt{total\_supply}$ + +3. Calculate the validator's restaking reward $R(v, T)$ using the reward function: + +$R(v, T) = \texttt{payout}(T) \times \left(\sum_{r} \alpha_r \times \texttt{dist}(v, r, T) + \left(1 - \sum_{r} \alpha_r\right) \times f_\texttt{restake}(v)\right)$ diff --git a/pages/docs/services/service-provider.mdx b/pages/docs/services/service-provider.mdx index 610b3fcf..c33e4b34 100644 --- a/pages/docs/services/service-provider.mdx +++ b/pages/docs/services/service-provider.mdx @@ -77,43 +77,6 @@ To opt into roles and restake TNT, validators should follow these steps: 3. **Restake Execution**: Allocate TNT to the selected roles through the Tangle Blockchain's interface using the appropriate dispatchable calls (detailed in Section 7). -## 5. Economic Incentives and Rewards - -The Tangle Blockchain employs a comprehensive rewards system to incentivize validators for their active participation and contributions to the network. - -### Rewards System Breakdown - -Validator rewards are calculated based on two main factors: - -1. **Active Validator Rewards (50%)**: Rewards for validators who have completed jobs in the current era. - - - The total jobs completed (TJC) by all active validators are calculated. - - Each validator's share (VS) of the rewards is determined by the proportion of their completed jobs relative to the TJC. - - The validator reward (VR) is computed as: VR = VS × Total Rewards - -2. **Validator Rewards by Restake (50%)**: Rewards for validators based on their restaked amount. - - The total restake (TR) in the system is calculated by summing all validators' restaked amounts. - - The restake-to-stake ratio (RSR) is computed as: RSR = TR / Total Stake in the Era - - The missing restake ratio (MRR) is calculated as: MRR = Maximum Restake Ratio - RSR - - The adjusted total rewards (ATR) are computed based on the MRR to ensure proper reward distribution. - - Each validator's reward share (RS) is determined by their restaked amount relative to the TR. - - The final reward for each validator is calculated as: Reward = RS × ATR - -#### Example Reward Calculation - -Let's consider an example in era 100, with 20 restakers and a total role reward of 1000 TNT: - -- **Active Validator Rewards**: - - - 10 restakers have completed 5 jobs each. - - 50% of the 1000 TNT (500 TNT) is equally divided among the 10 active restakers. - - If one restaker had completed more jobs than the others, they would receive a larger share of the rewards. - -- **Rewards by Restake**: - - The remaining 50% of the 1000 TNT (500 TNT) is distributed among all restakers based on their restaked amount. - - If a restaker has restaked 100 TNT out of a total restake of 1000 TNT, they are eligible for 10% of the rewards. - - The rewards are adjusted based on the total restake relative to the total stake in the system. - ### Capital Efficiency and Reward Optimization Restaking allows validators to earn rewards from multiple roles without requiring additional capital. This mechanism encourages efficient stake allocation and active participation in the network. By strategically allocating their staked TNT to various roles, validators can optimize their potential rewards while contributing to the network's security and functionality. @@ -156,8 +119,6 @@ The Tangle Blockchain provides a set of dispatchable calls for validators to man - `withdraw_unbonded`: Enables validators to withdraw unbonded funds after a certain period. These operations are only allowed when the validator has no active roles. -_Detailed examples and technical steps for each call will be provided in the technical documentation._ - ### Best Practices for Managing Validator Profiles To effectively manage their profiles and optimize their participation in the Tangle Blockchain, validators should: diff --git a/pages/docs/services/services.mdx b/pages/docs/services/services.mdx index f580a78d..ee87e76f 100644 --- a/pages/docs/services/services.mdx +++ b/pages/docs/services/services.mdx @@ -66,35 +66,3 @@ We're exploring the development of new privacy technologies for the future: - Threshold decryption as a service - Sequencing as a service - Randomness beacons - -# Tangle Restaking Rewards Documentation - -## Overview - -Tangle blockchain introduces a comprehensive restaking system embedded within its runtime environment. This system is designed to encourage validators to commit to multi-party computation (MPC) service roles by offering an array of rewards. This documentation provides a detailed overview of the restaking process, reward mechanisms, and the benefits for validators and nominators participating in the Tangle blockchain network. - -## Restaking Rewards - -- Validators on the Tangle blockchain can establish a restaking profile by allocating 50% of their locked and nominated stake towards restaking. -- Nominators associated with these validators receive proportional rewards, enhancing the benefits of participating in the restaking system. - -### Inflationary and Validation Rewards - -- To motivate validators to opt into MPC service roles, Tangle rewards them with additional inflationary rewards on top of their standard validation rewards, merely for restaking their tokens. - -### Performance and Service Fee Rewards - -- Validators are required to execute the services and tasks demanded by network users efficiently. Failure to fulfill these obligations results in penalties, including the slashing of their stake. -- Successful service execution leads to service fee rewards for validators, calculated proportionally to their restaked funds for the specific service rendered. - -### Scaling of Rewards - -- The rewards for restakers adjust proportionally based on several factors, including the amount of tokens restaked, the number of jobs completed, and the diversity of roles engaged in restaking. - -### Reward Function Structure - -- The reward distribution mechanism for restakers is bifurcated into two segments: - a) Active Participation Rewards: Rewards are based on the validator's participation in and completion of total job opportunities, benefiting the most active and reliable restakers. - b) Proportional Restaking Rewards: Rewards are allocated in proportion to the overall restaking distribution, maintaining the incentive for continuous restaking. The reward rate for this is programatically limited to a range of 0.1% and 2%, in a similar style to the core staking rewards system of Substrate blockchains. - -Tangle's restaking system is intricately designed to maximize the security and efficiency of MPC services by incentivizing validators through multiple reward avenues. This system not only rewards active job performance but also encourages widespread restaking, ensuring a robust and secure network. diff --git a/styles.css b/styles.css index 3a3be8de..650f87b2 100644 --- a/styles.css +++ b/styles.css @@ -3,10 +3,6 @@ @tailwind utilities; @tailwind variants; -/* Override the Tailwind preflight */ -button[type="submit"] { - @apply bg-black; -} @layer utilities { /* Hide scrollbar for Chrome, Safari and Opera */ diff --git a/yarn.lock b/yarn.lock index 8e251d18..c0c68da4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5591,6 +5591,11 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-hook-form@^7.51.1: + version "7.51.1" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.51.1.tgz#3ce5f8b5ef41903b4054a641cef8c0dc8bf8ae85" + integrity sha512-ifnBjl+kW0ksINHd+8C/Gp6a4eZOdWyvRv0UBaByShwU8JbVx5hTcTWEcd5VdybvmPTATkVVXk9npXArHmo56w== + react-hot-toast@2.4.0: version "2.4.0" resolved "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.0.tgz"