From bad135fba4a08c60d002c05c5b5582dfc781d753 Mon Sep 17 00:00:00 2001
From: marino <102478601+kemuru@users.noreply.github.com>
Date: Thu, 16 Nov 2023 00:32:00 +0100
Subject: [PATCH 1/3] feat(web): styling jurorinfo dashboard, add staking
rewards popup, mobile and desktop
---
web/src/assets/svgs/icons/close.svg | 3 +
web/src/assets/svgs/icons/kleros.svg | 2 +-
.../components/Popup/ClaimStakingRewards.tsx | 203 ++++++++++++++++++
.../Popup/Description/StakeWithdraw.tsx | 3 +
web/src/layout/Header/navbar/DappList.tsx | 8 +-
.../Dashboard/JurorInfo/JurorRewards.tsx | 1 +
.../Dashboard/JurorInfo/StakingRewards.tsx | 44 ++--
web/src/pages/Dashboard/JurorInfo/index.tsx | 44 ++--
8 files changed, 280 insertions(+), 28 deletions(-)
create mode 100644 web/src/assets/svgs/icons/close.svg
create mode 100644 web/src/components/Popup/ClaimStakingRewards.tsx
diff --git a/web/src/assets/svgs/icons/close.svg b/web/src/assets/svgs/icons/close.svg
new file mode 100644
index 000000000..ab7bfa063
--- /dev/null
+++ b/web/src/assets/svgs/icons/close.svg
@@ -0,0 +1,3 @@
+
diff --git a/web/src/assets/svgs/icons/kleros.svg b/web/src/assets/svgs/icons/kleros.svg
index e0dc1a0fd..a674a9235 100644
--- a/web/src/assets/svgs/icons/kleros.svg
+++ b/web/src/assets/svgs/icons/kleros.svg
@@ -1,3 +1,3 @@
diff --git a/web/src/components/Popup/ClaimStakingRewards.tsx b/web/src/components/Popup/ClaimStakingRewards.tsx
new file mode 100644
index 000000000..cb0404ab0
--- /dev/null
+++ b/web/src/components/Popup/ClaimStakingRewards.tsx
@@ -0,0 +1,203 @@
+import React, { useRef } from "react";
+import styled, { css } from "styled-components";
+import { landscapeStyle } from "styles/landscapeStyle";
+import { Card as _Card } from "@kleros/ui-components-library";
+import KlerosIcon from "svgs/icons/kleros.svg";
+import { useFocusOutside } from "hooks/useFocusOutside";
+import { Overlay } from "../Overlay";
+import RightArrow from "tsx:svgs/icons/arrow.svg";
+import RewardIcon from "tsx:svgs/icons/reward.svg";
+import CloseIcon from "tsx:svgs/icons/close.svg";
+
+const Container = styled.div`
+ display: flex;
+ position: relative;
+ width: 862px;
+ height: 636px;
+ flex-direction: column;
+ align-items: center;
+ background-color: ${({ theme }) => theme.whiteBackground};
+ padding: 52px;
+
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ max-height: 80vh;
+ overflow-y: auto;
+ z-index: 10;
+`;
+
+const StyledKlerosIcon = styled(KlerosIcon)`
+ path {
+ fill: ${({ theme }) => theme.secondaryPurple};
+ }
+ width: 112px;
+ height: 100px;
+`;
+
+const PnkQuantityText = styled.text`
+ font-size: 64px;
+ font-weight: 600;
+ color: ${({ theme }) => theme.secondaryPurple};
+ margin-top: 16px;
+`;
+
+const ThanksText = styled.text`
+ font-size: 24px;
+ font-weight: 600;
+ color: ${({ theme }) => theme.primaryText};
+ margin-top: 16px;
+`;
+
+const ExplanationText = styled.text`
+ font-size: 16px;
+ color: ${({ theme }) => theme.primaryText};
+ margin-top: 8px;
+`;
+
+const Card = styled(_Card)`
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ width: 100%;
+ min-height: 112px;
+ height: auto;
+ margin-top: 35px;
+ padding: 32px 24px;
+ gap: 4px;
+
+ ${landscapeStyle(() => css``)}
+`;
+
+const TotalClaimed = styled.div`
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+`;
+
+const Unclaimed = styled.div`
+ display: flex;
+ justify-content: space-between;
+ width: 100%;
+`;
+
+const AmountText = styled.label`
+ display: flex;
+ font-size: 16px;
+`;
+
+const TotalClaimedAmount = styled.small`
+ display: flex;
+ font-size: 16px;
+`;
+
+const UnclaimedAmount = styled.small`
+ display: flex;
+ color: ${({ theme }) => theme.secondaryPurple};
+ font-size: 16px;
+`;
+
+const ReadMore = styled.div`
+ display: flex;
+ color: ${({ theme }) => theme.primaryBlue};
+ font-size: 16px;
+ margin-top: 24px;
+ gap: 8px;
+ align-items: center;
+`;
+
+const ReadMoreLink = styled.a`
+ display: flex;
+ color: ${({ theme }) => theme.primaryBlue};
+ font-size: 16px;
+
+ &:hover {
+ text-decoration: underline;
+ }
+`;
+
+const StyledRightArrow = styled(RightArrow)`
+ path {
+ fill: ${({ theme }) => theme.primaryBlue};
+ }
+`;
+
+const StyledButton = styled.div`
+ display: flex;
+ width: 238px;
+ padding: 11px 0;
+ background-color: ${({ theme }) => theme.secondaryPurple};
+ margin-top: 36px;
+ color: ${({ theme }) => theme.whiteBackground};
+ font-size: 14px;
+ font-weight: 600;
+ justify-content: center;
+ align-items: center;
+ border-radius: 3px;
+ gap: 8px;
+ cursor: pointer;
+`;
+
+const StyledRewardIcon = styled(RewardIcon)`
+ path {
+ fill: ${({ theme }) => theme.whiteBackground};
+ }
+
+ width: 18px;
+ height: 16px;
+`;
+
+const StyledCloseIcon = styled(CloseIcon)`
+ position: absolute;
+ width: 18px;
+ height: 18px;
+`;
+
+interface IClaimStakingRewards {
+ toggleIsOpen: () => void;
+}
+
+const ClaimStakingRewards: React.FC = ({ toggleIsOpen }) => {
+ const containerRef = useRef(null);
+ useFocusOutside(containerRef, () => toggleIsOpen());
+
+ return (
+ <>
+
+
+
+ 1,000 PNK
+ 🎉 Thanks for being part of the community! 🎉
+ As a Kleros Juror, you will earn PNK for staking in Court.
+
+
+ Total Rewarded PNK:
+ 10,000 PNK
+
+
+ Unclaimed:
+ 1,000 PNK
+
+
+
+
+ Read more about the Juror Incentive Program
+
+
+
+
+
+ Claim Your PNK Tokens
+
+
+
+ >
+ );
+};
+
+export default ClaimStakingRewards;
diff --git a/web/src/components/Popup/Description/StakeWithdraw.tsx b/web/src/components/Popup/Description/StakeWithdraw.tsx
index e781a462b..078353bc6 100644
--- a/web/src/components/Popup/Description/StakeWithdraw.tsx
+++ b/web/src/components/Popup/Description/StakeWithdraw.tsx
@@ -13,6 +13,9 @@ const Container = styled.div`
`;
const StyledKlerosLogo = styled(KlerosLogo)`
+ path {
+ fill: ${({ theme }) => theme.secondaryPurple};
+ }
width: 14px;
height: 14px;
`;
diff --git a/web/src/layout/Header/navbar/DappList.tsx b/web/src/layout/Header/navbar/DappList.tsx
index 9aca796d5..4a361b646 100644
--- a/web/src/layout/Header/navbar/DappList.tsx
+++ b/web/src/layout/Header/navbar/DappList.tsx
@@ -58,6 +58,12 @@ const Container = styled.div`
)}
`;
+const StyledCourt = styled(Court)`
+ path {
+ fill: ${({ theme }) => theme.secondaryPurple};
+ }
+`;
+
const ItemsDiv = styled.div`
display: grid;
overflow-y: auto;
@@ -74,7 +80,7 @@ const ItemsDiv = styled.div`
const ITEMS = [
{
text: "Court v1",
- Icon: Court,
+ Icon: StyledCourt,
url: "https://court.kleros.io/",
},
{
diff --git a/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx b/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx
index 2c8653e5a..3df08ad89 100644
--- a/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx
+++ b/web/src/pages/Dashboard/JurorInfo/JurorRewards.tsx
@@ -13,6 +13,7 @@ const Container = styled.div`
flex-direction: column;
align-items: flex-start;
width: auto;
+ gap: calc(8px + (32 - 8) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
`;
const tooltipMsg =
diff --git a/web/src/pages/Dashboard/JurorInfo/StakingRewards.tsx b/web/src/pages/Dashboard/JurorInfo/StakingRewards.tsx
index 119d1d3e0..ffaee12a2 100644
--- a/web/src/pages/Dashboard/JurorInfo/StakingRewards.tsx
+++ b/web/src/pages/Dashboard/JurorInfo/StakingRewards.tsx
@@ -1,14 +1,17 @@
import React from "react";
import styled from "styled-components";
+import { useToggle } from "react-use";
import { Box as _Box, Button } from "@kleros/ui-components-library";
import TokenRewards from "./TokenRewards";
import WithHelpTooltip from "../WithHelpTooltip";
+import ClaimedStakingRewards from "components/Popup/ClaimedStakingRewards";
import { EnsureChain } from "components/EnsureChain";
const Container = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
+ gap: calc(8px + (32 - 8) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
`;
const Box = styled(_Box)`
@@ -16,9 +19,13 @@ const Box = styled(_Box)`
justify-content: space-between;
align-items: center;
padding: 8px;
- width: 270px;
+ padding-left: 20px;
+ width: calc(232px + (312 - 232) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
height: auto;
+ border: 1px solid ${({ theme }) => theme.stroke};
border-radius: 3px;
+ background-color: ${({ theme }) => theme.lightBlue};
+ gap: calc(12px + (28 - 12) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
`;
const UnclaimedContainer = styled.div`
@@ -27,25 +34,36 @@ const UnclaimedContainer = styled.div`
gap: 4px;
`;
+const StyledSmall = styled.small`
+ font-size: 16px;
+`;
+
const ClaimPNK: React.FC = () => {
+ const [isClaimRewardsModalOpen, toggleClaimRewardsModal] = useToggle(false);
+
return (
-
-
-
- 1,000 PNK
-
-
-
-
-
+ <>
+
+
+
+ 1,000 PNK
+
+
+
+
+
+ {isClaimRewardsModalOpen && }
+ >
);
};
const tooltipMsg =
"Staking Rewards are the rewards won by staking your PNK on a court during " +
- "the Kleros' Jurors incentive program.";
+ "the Kleros' Jurors incentive program." +
+ " APY means Annual Percentage Yield, and it is the rate of interest earned" +
+ " on your staked PNK in one year.";
-const Coherency: React.FC = () => {
+const StakingRewards: React.FC = () => {
return (
@@ -59,4 +77,4 @@ const Coherency: React.FC = () => {
);
};
-export default Coherency;
+export default StakingRewards;
diff --git a/web/src/pages/Dashboard/JurorInfo/index.tsx b/web/src/pages/Dashboard/JurorInfo/index.tsx
index 84eda39b9..ecce3a1de 100644
--- a/web/src/pages/Dashboard/JurorInfo/index.tsx
+++ b/web/src/pages/Dashboard/JurorInfo/index.tsx
@@ -9,7 +9,7 @@ import PixelArt from "./PixelArt";
import { useAccount } from "wagmi";
import { useUserQuery } from "queries/useUser";
import { getUserLevelData } from "utils/userLevelCalculation";
-// import StakingRewards from "./StakingRewards";
+import StakingRewards from "./StakingRewards";
const Container = styled.div``;
@@ -17,18 +17,33 @@ const Card = styled(_Card)`
display: flex;
flex-direction: column;
align-items: center;
- justify-content: center;
+ justify-content: flex-start;
+ flex-wrap: wrap;
- gap: 40px;
+ gap: 52px;
width: 100%;
height: auto;
- padding: 24px 0;
+ padding: 24px 32px;
${landscapeStyle(
() => css`
flex-direction: row;
- gap: calc(24px + (64 - 24) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
- height: 236px;
+ gap: 68px calc(24px + (96 - 24) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+ min-height: 236px;
+ `
+ )}
+`;
+
+const PixelArtAndCoherency = styled.div`
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ gap: 16px;
+
+ ${landscapeStyle(
+ () => css`
+ flex-direction: row;
+ gap: 32px;
`
)}
`;
@@ -51,14 +66,17 @@ const JurorInfo: React.FC = () => {
totalResolvedDisputes={totalResolvedDisputes}
/>
-
-
+
+
+
+
+
);
From 6684af3c4a983bca6620052f3b419c0d6df05251 Mon Sep 17 00:00:00 2001
From: marino <102478601+kemuru@users.noreply.github.com>
Date: Thu, 16 Nov 2023 03:47:39 +0100
Subject: [PATCH 2/3] feat(web): claimed staking rewards popup, modularized
---
.../components/Popup/ClaimStakingRewards.tsx | 203 ------------------
.../ClaimedStakingRewards/ClaimedText.tsx | 14 ++
.../Popup/ClaimedStakingRewards/Close.tsx | 24 +++
.../Popup/ClaimedStakingRewards/Divider.tsx | 16 ++
.../ClaimedStakingRewards/KlerosIcon.tsx | 16 ++
.../ClaimedStakingRewards/QuantityClaimed.tsx | 14 ++
.../Popup/ClaimedStakingRewards/ReadMore.tsx | 36 ++++
.../ClaimedStakingRewards/ThanksText.tsx | 13 ++
.../Popup/ClaimedStakingRewards/index.tsx | 63 ++++++
9 files changed, 196 insertions(+), 203 deletions(-)
delete mode 100644 web/src/components/Popup/ClaimStakingRewards.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/ClaimedText.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/Close.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/Divider.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/KlerosIcon.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/QuantityClaimed.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/ReadMore.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/ThanksText.tsx
create mode 100644 web/src/components/Popup/ClaimedStakingRewards/index.tsx
diff --git a/web/src/components/Popup/ClaimStakingRewards.tsx b/web/src/components/Popup/ClaimStakingRewards.tsx
deleted file mode 100644
index cb0404ab0..000000000
--- a/web/src/components/Popup/ClaimStakingRewards.tsx
+++ /dev/null
@@ -1,203 +0,0 @@
-import React, { useRef } from "react";
-import styled, { css } from "styled-components";
-import { landscapeStyle } from "styles/landscapeStyle";
-import { Card as _Card } from "@kleros/ui-components-library";
-import KlerosIcon from "svgs/icons/kleros.svg";
-import { useFocusOutside } from "hooks/useFocusOutside";
-import { Overlay } from "../Overlay";
-import RightArrow from "tsx:svgs/icons/arrow.svg";
-import RewardIcon from "tsx:svgs/icons/reward.svg";
-import CloseIcon from "tsx:svgs/icons/close.svg";
-
-const Container = styled.div`
- display: flex;
- position: relative;
- width: 862px;
- height: 636px;
- flex-direction: column;
- align-items: center;
- background-color: ${({ theme }) => theme.whiteBackground};
- padding: 52px;
-
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- max-height: 80vh;
- overflow-y: auto;
- z-index: 10;
-`;
-
-const StyledKlerosIcon = styled(KlerosIcon)`
- path {
- fill: ${({ theme }) => theme.secondaryPurple};
- }
- width: 112px;
- height: 100px;
-`;
-
-const PnkQuantityText = styled.text`
- font-size: 64px;
- font-weight: 600;
- color: ${({ theme }) => theme.secondaryPurple};
- margin-top: 16px;
-`;
-
-const ThanksText = styled.text`
- font-size: 24px;
- font-weight: 600;
- color: ${({ theme }) => theme.primaryText};
- margin-top: 16px;
-`;
-
-const ExplanationText = styled.text`
- font-size: 16px;
- color: ${({ theme }) => theme.primaryText};
- margin-top: 8px;
-`;
-
-const Card = styled(_Card)`
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 100%;
- min-height: 112px;
- height: auto;
- margin-top: 35px;
- padding: 32px 24px;
- gap: 4px;
-
- ${landscapeStyle(() => css``)}
-`;
-
-const TotalClaimed = styled.div`
- display: flex;
- justify-content: space-between;
- width: 100%;
-`;
-
-const Unclaimed = styled.div`
- display: flex;
- justify-content: space-between;
- width: 100%;
-`;
-
-const AmountText = styled.label`
- display: flex;
- font-size: 16px;
-`;
-
-const TotalClaimedAmount = styled.small`
- display: flex;
- font-size: 16px;
-`;
-
-const UnclaimedAmount = styled.small`
- display: flex;
- color: ${({ theme }) => theme.secondaryPurple};
- font-size: 16px;
-`;
-
-const ReadMore = styled.div`
- display: flex;
- color: ${({ theme }) => theme.primaryBlue};
- font-size: 16px;
- margin-top: 24px;
- gap: 8px;
- align-items: center;
-`;
-
-const ReadMoreLink = styled.a`
- display: flex;
- color: ${({ theme }) => theme.primaryBlue};
- font-size: 16px;
-
- &:hover {
- text-decoration: underline;
- }
-`;
-
-const StyledRightArrow = styled(RightArrow)`
- path {
- fill: ${({ theme }) => theme.primaryBlue};
- }
-`;
-
-const StyledButton = styled.div`
- display: flex;
- width: 238px;
- padding: 11px 0;
- background-color: ${({ theme }) => theme.secondaryPurple};
- margin-top: 36px;
- color: ${({ theme }) => theme.whiteBackground};
- font-size: 14px;
- font-weight: 600;
- justify-content: center;
- align-items: center;
- border-radius: 3px;
- gap: 8px;
- cursor: pointer;
-`;
-
-const StyledRewardIcon = styled(RewardIcon)`
- path {
- fill: ${({ theme }) => theme.whiteBackground};
- }
-
- width: 18px;
- height: 16px;
-`;
-
-const StyledCloseIcon = styled(CloseIcon)`
- position: absolute;
- width: 18px;
- height: 18px;
-`;
-
-interface IClaimStakingRewards {
- toggleIsOpen: () => void;
-}
-
-const ClaimStakingRewards: React.FC = ({ toggleIsOpen }) => {
- const containerRef = useRef(null);
- useFocusOutside(containerRef, () => toggleIsOpen());
-
- return (
- <>
-
-
-
- 1,000 PNK
- 🎉 Thanks for being part of the community! 🎉
- As a Kleros Juror, you will earn PNK for staking in Court.
-
-
- Total Rewarded PNK:
- 10,000 PNK
-
-
- Unclaimed:
- 1,000 PNK
-
-
-
-
- Read more about the Juror Incentive Program
-
-
-
-
-
- Claim Your PNK Tokens
-
-
-
- >
- );
-};
-
-export default ClaimStakingRewards;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/ClaimedText.tsx b/web/src/components/Popup/ClaimedStakingRewards/ClaimedText.tsx
new file mode 100644
index 000000000..729f82e90
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/ClaimedText.tsx
@@ -0,0 +1,14 @@
+import React from "react";
+import styled from "styled-components";
+
+const StyledText = styled.text`
+ font-size: calc(20px + (24 - 20) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+ font-weight: 600;
+ color: ${({ theme }) => theme.primaryText};
+ margin-top: 16px;
+`;
+
+const ClaimedText: React.FC = () => {
+ return 🎉 Claimed! 🎉;
+};
+export default ClaimedText;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/Close.tsx b/web/src/components/Popup/ClaimedStakingRewards/Close.tsx
new file mode 100644
index 000000000..c784d8637
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/Close.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import styled from "styled-components";
+import Icon from "tsx:svgs/icons/close.svg";
+
+const StyledIcon = styled(Icon)`
+ position: absolute;
+ width: 18px;
+ height: 18px;
+ align-self: flex-end;
+ cursor: pointer;
+
+ path {
+ fill: ${({ theme }) => theme.stroke};
+ }
+`;
+
+interface IClose {
+ togglePopup: () => void;
+}
+
+const Close: React.FC = ({ togglePopup }) => {
+ return CloseIcon;
+};
+export default Close;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/Divider.tsx b/web/src/components/Popup/ClaimedStakingRewards/Divider.tsx
new file mode 100644
index 000000000..388938795
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/Divider.tsx
@@ -0,0 +1,16 @@
+import React from "react";
+import styled from "styled-components";
+
+const StyledHr = styled.hr`
+ display: flex;
+ border: none;
+ height: 0.5px;
+ background-color: ${({ theme }) => theme.stroke};
+ margin: calc(8px + (18 - 8) * (min(max(100vw, 375px), 1250px) - 375px) / 875) 0px;
+ width: 100%;
+`;
+
+const Divider: React.FC = () => {
+ return ;
+};
+export default Divider;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/KlerosIcon.tsx b/web/src/components/Popup/ClaimedStakingRewards/KlerosIcon.tsx
new file mode 100644
index 000000000..58e856269
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/KlerosIcon.tsx
@@ -0,0 +1,16 @@
+import React from "react";
+import styled from "styled-components";
+import Icon from "svgs/icons/kleros.svg";
+
+const StyledIcon = styled(Icon)`
+ path {
+ fill: ${({ theme }) => theme.secondaryPurple};
+ }
+ width: calc(120px + (160 - 120) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+ height: calc(132px + (140 - 132) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+`;
+
+const KlerosIcon: React.FC = () => {
+ return ;
+};
+export default KlerosIcon;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/QuantityClaimed.tsx b/web/src/components/Popup/ClaimedStakingRewards/QuantityClaimed.tsx
new file mode 100644
index 000000000..177f36487
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/QuantityClaimed.tsx
@@ -0,0 +1,14 @@
+import React from "react";
+import styled from "styled-components";
+
+const StyledText = styled.text`
+ font-size: calc(40px + (64 - 40) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+ font-weight: 600;
+ color: ${({ theme }) => theme.secondaryPurple};
+ margin-top: 16px;
+`;
+
+const QuantityClaimed: React.FC = () => {
+ return 1,000 PNK;
+};
+export default QuantityClaimed;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/ReadMore.tsx b/web/src/components/Popup/ClaimedStakingRewards/ReadMore.tsx
new file mode 100644
index 000000000..a060c89d4
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/ReadMore.tsx
@@ -0,0 +1,36 @@
+import React from "react";
+import styled from "styled-components";
+import RightArrow from "tsx:svgs/icons/arrow.svg";
+
+const StyledLink = styled.a`
+ display: flex;
+ color: ${({ theme }) => theme.primaryBlue};
+ font-size: 16px;
+ margin-top: 8px;
+ align-items: center;
+ gap: 8px;
+
+ &:hover {
+ text-decoration: underline;
+ }
+`;
+
+const StyledRightArrow = styled(RightArrow)`
+ path {
+ fill: ${({ theme }) => theme.primaryBlue};
+ }
+`;
+
+const ReadMore: React.FC = () => {
+ return (
+
+ Read more about the Juror Incentive Program
+
+
+ );
+};
+export default ReadMore;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/ThanksText.tsx b/web/src/components/Popup/ClaimedStakingRewards/ThanksText.tsx
new file mode 100644
index 000000000..d47ddce42
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/ThanksText.tsx
@@ -0,0 +1,13 @@
+import React from "react";
+import styled from "styled-components";
+
+const StyledText = styled.text`
+ font-size: 16px;
+ color: ${({ theme }) => theme.primaryText};
+ margin-top: 16px;
+`;
+
+const ThanksText: React.FC = () => {
+ return Thank you for being part of the Kleros community.;
+};
+export default ThanksText;
diff --git a/web/src/components/Popup/ClaimedStakingRewards/index.tsx b/web/src/components/Popup/ClaimedStakingRewards/index.tsx
new file mode 100644
index 000000000..7baea3051
--- /dev/null
+++ b/web/src/components/Popup/ClaimedStakingRewards/index.tsx
@@ -0,0 +1,63 @@
+import React, { useRef } from "react";
+import styled, { css } from "styled-components";
+import { landscapeStyle } from "styles/landscapeStyle";
+import { useFocusOutside } from "hooks/useFocusOutside";
+import { Overlay } from "components/Overlay";
+import KlerosIcon from "./KlerosIcon";
+import ClaimedText from "./ClaimedText";
+import QuantityClaimed from "./QuantityClaimed";
+import Divider from "./Divider";
+import ThanksText from "./ThanksText";
+import ReadMore from "./ReadMore";
+import Close from "./Close";
+
+const Container = styled.div`
+ display: flex;
+ position: relative;
+ width: 86vw;
+ flex-direction: column;
+ align-items: center;
+ background-color: ${({ theme }) => theme.whiteBackground};
+ padding: calc(24px + (52 - 24) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+ padding-top: calc(24px + (48 - 24) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ max-height: 80vh;
+ overflow-y: auto;
+ z-index: 10;
+
+ ${landscapeStyle(
+ () => css`
+ width: calc(300px + (862 - 300) * (min(max(100vw, 375px), 1250px) - 375px) / 875);
+ `
+ )}
+`;
+
+interface IClaimedStakingRewards {
+ toggleIsOpen: () => void;
+}
+
+const ClaimedStakingRewards: React.FC = ({ toggleIsOpen }) => {
+ const containerRef = useRef(null);
+ useFocusOutside(containerRef, () => toggleIsOpen());
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ClaimedStakingRewards;
From ec4c0afd3b5cd9558ed9cf123cd23f5e0e3eb0e4 Mon Sep 17 00:00:00 2001
From: jaybuidl
Date: Tue, 5 Dec 2023 14:29:48 +0000
Subject: [PATCH 3/3] feat: added MerkleRedeem contract migrated to Solidity
v0.8
---
contracts/deploy/00-home-chain-arbitration.ts | 6 +
contracts/src/arbitration/MerkleRedeem.sol | 159 ++++++++++++++++++
2 files changed, 165 insertions(+)
create mode 100644 contracts/src/arbitration/MerkleRedeem.sol
diff --git a/contracts/deploy/00-home-chain-arbitration.ts b/contracts/deploy/00-home-chain-arbitration.ts
index bf5b961bb..e1a1372d6 100644
--- a/contracts/deploy/00-home-chain-arbitration.ts
+++ b/contracts/deploy/00-home-chain-arbitration.ts
@@ -119,6 +119,12 @@ const deployArbitration: DeployFunction = async (hre: HardhatRuntimeEnvironment)
await execute("KlerosCore", { from: deployer, log: true }, "changeCurrencyRates", pnk, 12225583, 12);
await execute("KlerosCore", { from: deployer, log: true }, "changeCurrencyRates", dai, 60327783, 11);
await execute("KlerosCore", { from: deployer, log: true }, "changeCurrencyRates", weth, 1, 1);
+
+ await deploy("MerkleRedeem", {
+ from: deployer,
+ args: [pnk],
+ log: true,
+ });
};
deployArbitration.tags = ["Arbitration"];
diff --git a/contracts/src/arbitration/MerkleRedeem.sol b/contracts/src/arbitration/MerkleRedeem.sol
new file mode 100644
index 000000000..ad8ffaa38
--- /dev/null
+++ b/contracts/src/arbitration/MerkleRedeem.sol
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: MIT
+
+/**
+ * Original code taken from: https://github.com/balancer-labs/erc20-redeemable/blob/13d478a043ec7bfce7abefe708d027dfe3e2ea84/merkle/contracts/MerkleRedeem.sol
+ * Only comments and events were added, some variable names changed for clarity and the compiler version was upgraded to 0.8.x.
+ */
+pragma solidity 0.8.18;
+
+import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
+import "@openzeppelin/contracts/access/Ownable.sol";
+import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+/// @title Distribution of tokens in a recurrent fashion.
+contract MerkleRedeem is Ownable {
+ // ************************************* //
+ // * Enums / Structs * //
+ // ************************************* //
+
+ struct Claim {
+ uint week; // The week the claim is related to.
+ uint balance; // The amount being claimed.
+ bytes32[] merkleProof; // The merkle proof for the claim, sorted from the leaf to the root of the tree.
+ }
+
+ // ************************************* //
+ // * Events * //
+ // ************************************* //
+
+ /// @dev To be emitted when a claim is made.
+ /// @param _claimant The address of the claimant.
+ /// @param _balance The amount being claimed.
+ event Claimed(address _claimant, uint256 _balance);
+
+ // ************************************* //
+ // * Storage * //
+ // ************************************* //
+
+ IERC20 public token; // The address of the token being distributed.
+ mapping(uint => bytes32) public weekMerkleRoots; // The merkle roots of each week. weekMerkleRoots[week].
+ mapping(uint => mapping(address => bool)) public claimed; // Keeps track of the claim status for the given period and claimant. claimed[period][claimant].
+
+ // ************************************* //
+ // * Constructor * //
+ // ************************************* //
+
+ /// @param _token The address of the token being distributed.
+ constructor(address _token) {
+ token = IERC20(_token);
+ }
+
+ // ************************************* //
+ // * State Modifiers * //
+ // ************************************* //
+
+ /// @notice Seeds a new round for the airdrop.
+ /// @dev Will transfer tokens from the owner to this contract.
+ /// @param _week The airdrop week.
+ /// @param _merkleRoot The merkle root of the claims for that period.
+ /// @param _totalAllocation The amount of tokens allocated for the distribution.
+ function seedAllocations(uint _week, bytes32 _merkleRoot, uint _totalAllocation) external onlyOwner {
+ require(weekMerkleRoots[_week] == bytes32(0), "cannot rewrite merkle root");
+ weekMerkleRoots[_week] = _merkleRoot;
+
+ require(token.transferFrom(msg.sender, address(this), _totalAllocation), "ERR_TRANSFER_FAILED");
+ }
+
+ /// @notice Makes a claim for a given claimant in a week.
+ /// @param _liquidityProvider The address of the claimant.
+ /// @param _week The week for the claim.
+ /// @param _claimedBalance The amount being claimed.
+ /// @param _merkleProof The merkle proof for the claim, sorted from the leaf to the root of the tree.
+ function claimWeek(
+ address _liquidityProvider,
+ uint _week,
+ uint _claimedBalance,
+ bytes32[] memory _merkleProof
+ ) public {
+ require(!claimed[_week][_liquidityProvider]);
+ require(verifyClaim(_liquidityProvider, _week, _claimedBalance, _merkleProof), "Incorrect merkle proof");
+
+ claimed[_week][_liquidityProvider] = true;
+ disburse(_liquidityProvider, _claimedBalance);
+ }
+
+ /// @notice Makes multiple claims for a given claimant.
+ /// @param _liquidityProvider The address of the claimant.
+ /// @param claims An array of claims containing the week, balance and the merkle proof.
+ function claimWeeks(address _liquidityProvider, Claim[] memory claims) public {
+ uint totalBalance = 0;
+ Claim memory claim;
+ for (uint i = 0; i < claims.length; i++) {
+ claim = claims[i];
+
+ require(!claimed[claim.week][_liquidityProvider]);
+ require(
+ verifyClaim(_liquidityProvider, claim.week, claim.balance, claim.merkleProof),
+ "Incorrect merkle proof"
+ );
+
+ totalBalance += claim.balance;
+ claimed[claim.week][_liquidityProvider] = true;
+ }
+ disburse(_liquidityProvider, totalBalance);
+ }
+
+ /// @notice Gets the claim status for given claimant from `_begin` to `_end` weeks.
+ /// @param _liquidityProvider The address of the claimant.
+ /// @param _begin The week to start with (inclusive).
+ /// @param _end The week to end with (inclusive).
+ function claimStatus(address _liquidityProvider, uint _begin, uint _end) external view returns (bool[] memory) {
+ uint size = 1 + _end - _begin;
+ bool[] memory arr = new bool[](size);
+ for (uint i = 0; i < size; i++) {
+ arr[i] = claimed[_begin + i][_liquidityProvider];
+ }
+ return arr;
+ }
+
+ /// @notice Gets all merkle roots for from `_begin` to `_end` weeks.
+ /// @param _begin The week to start with (inclusive).
+ /// @param _end The week to end with (inclusive).
+ function merkleRoots(uint _begin, uint _end) external view returns (bytes32[] memory) {
+ uint size = 1 + _end - _begin;
+ bytes32[] memory arr = new bytes32[](size);
+ for (uint i = 0; i < size; i++) {
+ arr[i] = weekMerkleRoots[_begin + i];
+ }
+ return arr;
+ }
+
+ /// @notice Verifies a claim.
+ /// @param _liquidityProvider The address of the claimant.
+ /// @param _week The week for the claim.
+ /// @param _claimedBalance The amount being claimed.
+ /// @param _merkleProof The merkle proof for the claim, sorted from the leaf to the root of the tree.
+ function verifyClaim(
+ address _liquidityProvider,
+ uint _week,
+ uint _claimedBalance,
+ bytes32[] memory _merkleProof
+ ) public view returns (bool valid) {
+ bytes32 leaf = keccak256(abi.encodePacked(_liquidityProvider, _claimedBalance));
+ return MerkleProof.verify(_merkleProof, weekMerkleRoots[_week], leaf);
+ }
+
+ // ************************************* //
+ // * Internal * //
+ // ************************************* //
+
+ /// @dev Effectively pays a claimant.
+ /// @param _liquidityProvider The address of the claimant.
+ /// @param _balance The amount being claimed.
+ function disburse(address _liquidityProvider, uint _balance) private {
+ if (_balance > 0) {
+ emit Claimed(_liquidityProvider, _balance);
+ require(token.transfer(_liquidityProvider, _balance), "ERR_TRANSFER_FAILED");
+ }
+ }
+}