Skip to content

Commit

Permalink
Merge branch 'staging' into stable
Browse files Browse the repository at this point in the history
  • Loading branch information
shahthepro committed Jul 6, 2023
2 parents 8720011 + cb0f920 commit c1a7bd5
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 80 deletions.
18 changes: 5 additions & 13 deletions client/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,16 @@ import Wrapper from "components/Wrapper";
import Link from "components/Link";
import Image from "next/image";
import { navItems } from "../constants";
import useClaim from "utils/useClaim";
import { getRewardsApy } from "utils/apy";
import useStakingAPY from "utils/useStakingAPY";

interface HeaderProps {
hideNav?: boolean;
}

const Header: FunctionComponent<HeaderProps> = ({ hideNav }) => {
const [menuIsOpen, setMenuIsOpen] = useState(false);
const claim = useClaim();

const totalSupplyVeOgv = claim.staking.totalSupplyVeOgvAdjusted || 0;

// Standard APY figure, assumes 100 OGV locked for max duration
const stakingApy = getRewardsApy(
100 * 1.8 ** (48 / 12),
100,
totalSupplyVeOgv
);
const { stakingAPY, loading: apyLoading } = useStakingAPY(100, 48);

const overlayClassNames = classNames(
"bg-black z-20 h-screen w-screen fixed top-0 transition duration-200 lg:hidden",
Expand Down Expand Up @@ -80,7 +71,8 @@ const Header: FunctionComponent<HeaderProps> = ({ hideNav }) => {
"
/>
<span className="text-xs bg-white bg-opacity-10 px-2 py-[0.2rem] rounded-sm font-bold">
{stakingApy.toFixed(2)}% vAPY
{apyLoading ? "--.--" : stakingAPY.toFixed(2)}%
vAPY
</span>
</div>
)}
Expand Down Expand Up @@ -152,7 +144,7 @@ const Header: FunctionComponent<HeaderProps> = ({ hideNav }) => {
"
/>
<span className="text-xs bg-primary bg-opacity-100 px-2 py-[0.2rem] rounded-sm !font-light text-white">
{stakingApy.toFixed(2)}% vAPY
{apyLoading ? "--.--" : stakingAPY.toFixed(2)}% vAPY
</span>
</div>
)}
Expand Down
20 changes: 8 additions & 12 deletions client/components/claim/Eligibility.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import Icon from "@mdi/react";
import { mdiWallet, mdiCheckCircle, mdiAlertCircle } from "@mdi/js";
import { Loading } from "components/Loading";
import Link from "components/Link";
import { getRewardsApy } from "utils/apy";
import { filter } from "lodash";
import { BigNumber } from "ethers";
import useStakingAPY from "utils/useStakingAPY";

interface EligibilityProps {
handleNextStep: () => void;
Expand All @@ -34,14 +33,8 @@ const Eligibility: FunctionComponent<EligibilityProps> = ({
split.gte(0)
).length;

const totalSupplyVeOgv = claim.staking.totalSupplyVeOgvAdjusted || 0;

// Standard APY figure, assumes 100 OGV locked for max duration
const stakingApy = getRewardsApy(
100 * 1.8 ** (48 / 12),
100,
totalSupplyVeOgv
);
const { stakingAPY, loading: apyLoading } = useStakingAPY(100, 48);

const claimValid =
(hasClaim && claim.optional && claim.optional.isValid) ||
Expand Down Expand Up @@ -148,8 +141,9 @@ const Eligibility: FunctionComponent<EligibilityProps> = ({
</div>
<div className="pt-9 space-y-4">
<p className="text-2xl">
OGV stakers earn a {stakingApy.toFixed(2)}% variable
APY
OGV stakers earn a{" "}
{apyLoading ? "--.--" : stakingAPY.toFixed(2)}%
variable APY
</p>
<Link
href="https://app.uniswap.org/#/swap?outputCurrency=0x9c354503C38481a7A7a51629142963F98eCC12D0&chain=mainnet"
Expand Down Expand Up @@ -189,7 +183,9 @@ const Eligibility: FunctionComponent<EligibilityProps> = ({
</div>
<div className="pt-9 space-y-4">
<p className="text-2xl">
OGV stakers earn a {stakingApy.toFixed(2)}% variable APY
OGV stakers earn a{" "}
{apyLoading ? "--.--" : stakingAPY.toFixed(2)}% variable
APY
</p>
<Link
href="https://app.uniswap.org/#/swap?outputCurrency=0x9c354503C38481a7A7a51629142963F98eCC12D0&chain=mainnet"
Expand Down
34 changes: 18 additions & 16 deletions client/components/claim/claim/ClaimOgv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Button from "components/Button";
import RangeInput from "@/components/RangeInput";
import useClaim from "utils/useClaim";
import numeral from "numeraljs";
import { getRewardsApy } from "utils/apy";
import { decimal18Bn } from "utils";
import PostClaimModal from "./PostClaimModal";
import Icon from "@mdi/react";
Expand All @@ -18,6 +17,7 @@ import { SECONDS_IN_A_MONTH } from "../../../constants/index";
import ApyToolTip from "components/claim/claim/ApyTooltip";
import moment from "moment";
import { useStore } from "utils/store";
import useStakingAPY from "utils/useStakingAPY";

interface ClaimOgvProps {}

Expand All @@ -33,7 +33,6 @@ const ClaimOgv: FunctionComponent<ClaimOgvProps> = () => {
maxLockupDurationInMonths
);
const [error, setError] = useState<string>(null);
const totalSupplyVeOgv = claim.staking.totalSupplyVeOgvAdjusted || 0;
if (!claim.loaded || !claim.optional.hasClaim) {
return <></>;
}
Expand All @@ -47,17 +46,14 @@ const ClaimOgv: FunctionComponent<ClaimOgvProps> = () => {

const veOgvFromOgvLockup =
claimableOgv * votingDecayFactor ** (lockupDuration / 12);
const ogvLockupRewardApy = getRewardsApy(
veOgvFromOgvLockup,
claimableOgv,
totalSupplyVeOgv
);
const maxVeOgvFromOgvLockup = claimableOgv * votingDecayFactor ** (48 / 12);
const maxOgvLockupRewardApy = getRewardsApy(
maxVeOgvFromOgvLockup,
claimableOgv,
totalSupplyVeOgv
);

const { stakingAPY: ogvLockupRewardApy, loading: ogvLockupRewardLoading } =
useStakingAPY(claimableOgv, lockupDuration);

const {
stakingAPY: maxOgvLockupRewardApy,
loading: maxOgvLockupRewardLoading,
} = useStakingAPY(claimableOgv, 48);

let claimButtonText = "";
if (isValidLockup && claim.optional.state === "ready") {
Expand Down Expand Up @@ -134,7 +130,11 @@ const ClaimOgv: FunctionComponent<ClaimOgvProps> = () => {
<div className="flex p-2 flex-col sm:items-end">
<div className="flex space-x-2 items-end">
<CardStat large>
{isValidLockup ? ogvLockupRewardApy.toFixed(2) : 0}
{isValidLockup
? ogvLockupRewardLoading
? "--.--"
: ogvLockupRewardApy.toFixed(2)
: 0}
</CardStat>
<CardDescription large>%</CardDescription>
</div>
Expand Down Expand Up @@ -270,8 +270,10 @@ const ClaimOgv: FunctionComponent<ClaimOgvProps> = () => {
{!isValidLockup && (
<div className="p-6 bg-[#dd0a0a1a] border border-[#dd0a0a] rounded-lg text-xl text-center text-[#dd0a0a]">
If you don&apos;t stake your OGV, you&apos;ll miss out on the{" "}
{maxOgvLockupRewardApy.toFixed(2)}% variable APY and maximized
voting power.{" "}
{maxOgvLockupRewardLoading
? "--.--"
: maxOgvLockupRewardApy.toFixed(2)}
% variable APY and maximized voting power.{" "}
<TokenAmount amount={totalLockedUpOgv} format="currency" /> OGV (
{totalPercentageOfLockedUpOgv.toFixed(2)}% of the total supply)
has already been staked by other users.
Expand Down
29 changes: 11 additions & 18 deletions client/components/vote-escrow/LockupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import moment from "moment";
import { mdiInformationOutline as InfoIcon } from "@mdi/js";
import Icon from "@mdi/react";
import ApyToolTip from "components/claim/claim/ApyTooltip";
import { getRewardsApy } from "utils/apy";
import numeral from "numeraljs";
import { decimal18Bn } from "utils";
import useStakingAPY from "utils/useStakingAPY";
import classnames from "classnames";

interface LockupFormProps {
Expand Down Expand Up @@ -124,12 +124,9 @@ const LockupForm: FunctionComponent<LockupFormProps> = ({ existingLockup }) => {
balances,
allowances,
blockTimestamp,
totalBalances,
} = useStore();
const router = useRouter();

const { totalSupplyVeOgvAdjusted } = totalBalances;

const [lockupAmount, setLockupAmount] = useState(
existingLockup
? Math.floor(
Expand All @@ -143,17 +140,11 @@ const LockupForm: FunctionComponent<LockupFormProps> = ({ existingLockup }) => {
: Math.floor((existingLockup.end - blockTimestamp) / SECONDS_IN_A_MONTH)
); // In months

// as specified here: https://github.com/OriginProtocol/ousd-governance/blob/master/contracts/OgvStaking.sol#L21
const votingDecayFactor = 1.8;

const veOgvFromOgvLockup =
lockupAmount * votingDecayFactor ** (lockupDuration / 12);

const ogvLockupRewardApy = getRewardsApy(
veOgvFromOgvLockup,
lockupAmount,
totalSupplyVeOgvAdjusted
);
const {
stakingAPY: ogvLockupRewardApy,
loading: apyLoading,
veOgvReceived: veOgvFromOgvLockup,
} = useStakingAPY(lockupAmount, lockupDuration);

const validLockup = lockupAmount !== "0" && lockupDuration !== "0";

Expand Down Expand Up @@ -463,7 +454,9 @@ const LockupForm: FunctionComponent<LockupFormProps> = ({ existingLockup }) => {
<div className="flex space-x-2 items-end">
<CardStat large>
{validLockup
? ogvLockupRewardApy.toFixed(2)
? apyLoading
? "--.--"
: ogvLockupRewardApy.toFixed(2)
: (0.0).toFixed(2)}
</CardStat>
<span className="text-neutral text-sm">%</span>
Expand All @@ -476,7 +469,7 @@ const LockupForm: FunctionComponent<LockupFormProps> = ({ existingLockup }) => {
<h2 className="text-2xl font-bold font-header">Your stake summary</h2>
<CardGroup horizontal twoCol>
<div className="flex flex-col space-y-4">
<span className="text-sm text-neutral">Variable APY</span>
<span className="text-sm text-neutral">Amount locked</span>
<Card tightPadding noShadow className="!border-1 !border-neutral">
<div className="flex flex-col justify-center">
<div className="flex space-x-[0.4rem] items-end">
Expand All @@ -497,7 +490,7 @@ const LockupForm: FunctionComponent<LockupFormProps> = ({ existingLockup }) => {
</Card>
</div>
<div className="flex flex-col space-y-4">
<span className="text-sm text-neutral">Variable APY</span>
<span className="text-sm text-neutral">Voting power</span>
<Card tightPadding noShadow className="!border-1 !border-neutral">
<div className="flex flex-col justify-center">
<div className="flex space-x-[0.4rem] items-end">
Expand Down
14 changes: 3 additions & 11 deletions client/components/vote-escrow/YourLockups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import { useStore } from "utils/store";
import { Loading } from "components/Loading";
import { toast } from "react-toastify";
import useAccountBalances from "utils/useAccountBalances";
import useClaim from "utils/useClaim";
import { getRewardsApy } from "utils/apy";
import Image from "next/image";
import DisabledButtonToolTip from "../DisabledButtonTooltip";
import LockupsTable from "./LockupsTable";
import { Web3Button } from "components/Web3Button";
import useStakingAPY from "utils/useStakingAPY";

interface YourLockupsProps {}

Expand All @@ -28,7 +27,6 @@ const YourLockups: FunctionComponent<YourLockupsProps> = () => {
const { ogv, accruedRewards } = balances;
const { totalPercentageOfLockedUpOgv } = totalBalances;
const { reloadAccountBalances } = useAccountBalances();
const claim = useClaim();
const [collectRewardsStatus, setCollectRewardsStatus] = useState("ready");

let collectRewardsButtonText = "";
Expand All @@ -40,14 +38,8 @@ const YourLockups: FunctionComponent<YourLockupsProps> = () => {
collectRewardsButtonText = "Waiting to be mined";
}

const totalSupplyVeOgv = claim.staking.totalSupplyVeOgvAdjusted || 0;

// Standard APY figure, assumes 100 OGV locked for max duration
const stakingApy = getRewardsApy(
100 * 1.8 ** (48 / 12),
100,
totalSupplyVeOgv
);
const { stakingAPY, loading: apyLoading } = useStakingAPY(100, 48);

if (!lockups) {
return (
Expand Down Expand Up @@ -123,7 +115,7 @@ const YourLockups: FunctionComponent<YourLockupsProps> = () => {
<span className="block pt-2">
OGV stakers earn{" "}
<span className="font-bold bg-secondary px-[4px] py-[1px] rounded gradient-link-alt">
{stakingApy.toFixed(2)}%
{apyLoading ? "--.--" : stakingAPY.toFixed(2)}%
</span>{" "}
variable APY
</span>
Expand Down
16 changes: 6 additions & 10 deletions client/pages/claim.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@ import Wrapper from "components/Wrapper";
import Seo from "components/Seo";
import { PageTitle } from "components/PageTitle";
import Card from "components/Card";
import Button from "components/Button";
import useClaim from "utils/useClaim";
import { getRewardsApy } from "utils/apy";
import Link from "components/Link";
import useStakingAPY from "utils/useStakingAPY";

interface ClaimPageProps {}

const ClaimPage: NextPage<ClaimPageProps> = () => {
const claim = useClaim();
const totalSupplyVeOgv = claim.staking.totalSupplyVeOgvAdjusted || 0;
// Standard APY figure, assumes 100 OGV locked for max duration
const stakingApy = getRewardsApy(
100 * 1.8 ** (48 / 12),
100,
totalSupplyVeOgv
);
const { stakingAPY, loading: apyLoading } = useStakingAPY(100, 48);

return (
<Wrapper narrow>
Expand All @@ -29,9 +25,9 @@ const ClaimPage: NextPage<ClaimPageProps> = () => {
The claim period has now expired
</h2>
<div className="mt-4 text-sm">
{`All unclaimed OGV was burned after 90 days. You can still buy OGV and stake it for up to four years. Current OGV stakers are earning a vAPY of ${stakingApy.toFixed(
2
)}%.`}
{`All unclaimed OGV was burned after 90 days. You can still buy OGV and stake it for up to four years. Current OGV stakers are earning a vAPY of ${
apyLoading ? "--.--" : stakingAPY.toFixed(2)
}%.`}
</div>
<div className="mt-10">
<Link
Expand Down
58 changes: 58 additions & 0 deletions client/utils/useStakingAPY.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useEffect, useMemo, useState } from "react";
import { SECONDS_IN_A_MONTH } from "constants/index";
import { useStore } from "utils/store";
import { getRewardsApy } from "./apy";
import { ethers } from "ethers";

const useStakingAPY = (amountStaked, duration) => {
const { contracts, totalBalances } = useStore();

const [loading, setLoading] = useState(true);
const [veOgvReceived, setVeOGVReceived] = useState(0);

const { totalSupplyVeOgvAdjusted } = totalBalances;

useEffect(() => {
if (!contracts.loaded) return;

let done = false;

async function go() {
try {
const [val] = await contracts.OgvStaking.previewPoints(
ethers.utils.parseEther(amountStaked.toString()),
duration * SECONDS_IN_A_MONTH
);

if (!done) {
setVeOGVReceived(parseFloat(ethers.utils.formatEther(val)));
setLoading(false);
}
} catch (err) {
console.error("Failed to fetch APY", err);
}
}

go();

return () => {
done = true;
};
}, [amountStaked, duration, contracts.loaded]);

const stakingAPY = useMemo(() => {
if (!veOgvReceived || !totalSupplyVeOgvAdjusted || !amountStaked) {
return 0;
}

return getRewardsApy(veOgvReceived, amountStaked, totalSupplyVeOgvAdjusted);
}, [veOgvReceived, amountStaked, totalSupplyVeOgvAdjusted]);

return {
loading,
veOgvReceived,
stakingAPY,
};
};

export default useStakingAPY;

0 comments on commit c1a7bd5

Please sign in to comment.