Skip to content

Commit

Permalink
Feat: add veyfi calculator (#493)
Browse files Browse the repository at this point in the history
* add RPC calls and checks

* add script and license, fix imports

* clean up react functions.

* update workflow

* update workflow

* update workflow

* remove automatic address checking on load.

* a start

* kinda working veyfi calculator

* nicely scaling chart1

* both charts are working

* add sidebar

* improve chart shit

* units fix

* clean up component

* styling and change to min boost change

* update x axis labels

* add api calls

* turn on ypricemagic call

* add USD/Shares toggle

* add veYFI lock calculator

* fix smol bug

* fix tooltip color in dark mode

* fix: add back yDaemon endpoint
  • Loading branch information
rossgalloway authored Jan 21, 2025
1 parent c38618b commit c354075
Show file tree
Hide file tree
Showing 31 changed files with 3,371 additions and 11 deletions.
1 change: 1 addition & 0 deletions .github/workflows/addressChecks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
cat scripts/fetchedAddressData.json >> issue_body.md
fi
- name: Create an issue if checks failed
if: env.all_checks_passed != 'true'
uses: peter-evans/create-issue-from-file@v4
Expand Down
3 changes: 3 additions & 0 deletions docs/contributing/governance/veyfi-calculator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# veYFI Boost Calculator

<VeYFICalculator />
4 changes: 4 additions & 0 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import 'dotenv/config'
const branchName = process.env.BRANCH_NAME || 'unknown'
const isDev = process.env.IS_DEV === 'true'
const alchemyKey = process.env.ALCHEMY_API_KEY || 'unknown'
const yDaemon = process.env.YDAEMON_ENDPOINT || 'unknown'
const yPriceMagic = process.env.YPRICEMAGIC_ENDPOINT || 'unknown'

export default {
title: 'Yearn Docs',
Expand All @@ -26,6 +28,8 @@ export default {
branchName,
isDev,
alchemyKey,
yDaemon,
yPriceMagic,
},
themes: ['@docusaurus/theme-mermaid'],
themeConfig: {
Expand Down
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@
"@docusaurus/preset-classic": "3.5.2",
"@docusaurus/theme-mermaid": "3.5.2",
"@mdx-js/react": "^3.0.1",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.4",
"clsx": "1.1.1",
"dotenv": "^16.4.5",
"hast-util-is-element": "1.1.0",
"lucide-react": "^0.465.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"recharts": "^2.14.1",
"rehype-katex": "7",
"remark-math": "6",
"solc": "^0.8.17",
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ semantic-version==2.10.0
six==1.16.0
solc-select==1.0.4
urllib3==2.2.3
vyper==0.3.7
vyper==0.3.7
# ypricemagic==4.0.52
3 changes: 3 additions & 0 deletions scripts/runAddressChecks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const fetchAddresses = async () => {
failedChecks
)
v3CheckFlag = topLevelData?.checkFlag

if (!topLevelData)
throw new Error('Failed to fetch top-level contract addresses')

Expand All @@ -49,6 +50,7 @@ const fetchAddresses = async () => {
failedChecks
)
v3CheckFlag = protocolPeripheryData?.checkFlag

if (!protocolPeripheryData || !protocolPeripheryData?.addresses)
throw new Error('Failed to fetch protocol addresses')

Expand All @@ -72,6 +74,7 @@ const fetchAddresses = async () => {
if (!yearnV3Data) throw new Error('Failed to fetch Yearn V3 addresses')

const v3AddressData: V3ContractAddresses = {

topLevel: topLevelData.addresses,
protocolPeriphery: protocolPeripheryData.addresses,
releaseRegistry: releaseRegistryData.addresses,
Expand Down
1 change: 1 addition & 0 deletions sidebars/sidebarsContributing.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module.exports = {
},
items: [
'governance/veYFI-comp-summary',
'governance/veyfi-calculator',
{
type: 'link',
label: 'Contract Addresses →',
Expand Down
100 changes: 100 additions & 0 deletions src/components/VeYFILockCalculator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useState, useEffect } from 'react'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from './shadcn/card/card'
import Input from './shadcn/input/input'
import styles from '../css/veYFI-calc.module.css'

const VeYFILockCalculator = ({ onVeYFIChange }) => {
// Accept callback prop
const [YfiToLock, setYfiToLock] = useState(0)
const [YfiLockTime, setYfiLockTime] = useState(0)
const [veYFI, setVeYFI] = useState(0)

const handleYfiToLockChange = (event) => {
setYfiToLock(event.target.value)
}

const handleYfiLockTimeChange = (event) => {
setYfiLockTime(event.target.value)
}

const calculateVeYFI = () => {
const lockTime = Math.min(YfiLockTime / 365, 4) // clamp YfiLockTime to max 4 years
return YfiToLock * (lockTime / 4)
}

const formatLockTime = (days) => {
const years = Math.floor(days / 365)
const months = Math.floor((days % 365) / 30)
const remainingDays = (days % 365) % 30
return `${years} years, ${months} months, ${remainingDays} days`
}

useEffect(() => {
const newVeYFI = calculateVeYFI()
setVeYFI(newVeYFI)
onVeYFIChange(newVeYFI) // Call the callback with the new veYFI value
}, [YfiToLock, YfiLockTime])

return (
<Card style={{ marginTop: '2rem' }}>
<CardHeader>
<CardTitle>Calculate veYFI from locking YFI</CardTitle>
<CardDescription>
Enter the amount of YFI and the duration to lock veYFI. {''}
<a
href="https://docs.yearn.fi/contributing/governance/veYFI-comp-summary#locking-yfi-for-veyfi"
target="_blank"
rel="noopener noreferrer"
>
More info
</a>
</CardDescription>
</CardHeader>
<CardContent className={styles.CardContent}>
<div className={styles.inputElements}>
<div
style={{
display: 'flex',
flexDirection: 'column',
width: '100%',
}}
>
<label>YFI to Lock</label>
<Input
type="number"
value={YfiToLock}
onChange={handleYfiToLockChange}
/>
</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
width: '100%',
}}
>
<div>Lock Time: {formatLockTime(YfiLockTime)}</div>
<Input
type="range"
min="0"
max="2920"
value={YfiLockTime}
onChange={handleYfiLockTimeChange}
/>
</div>
</div>
<div style={{ marginBottom: '2rem' }}>
You will get <strong>{veYFI}</strong> veYFI
</div>
</CardContent>
</Card>
)
}

export default VeYFILockCalculator
29 changes: 29 additions & 0 deletions src/components/shadcn/button/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import styles from './index.module.css'

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
asChild?: boolean;
variant?: 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link' | 'icon'
size?: 'sm' | 'lg' | 'icon'
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ variant, className, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
data-size={size}
data-variant={variant}
className={`${styles.Button} ${className ? className : ''}`}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"

export { Button };
export type { ButtonProps };

139 changes: 139 additions & 0 deletions src/components/shadcn/button/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
.Button {
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 600;
outline: none;
background-color: var(--ifm-color-primary);
color: white;
border: 1px solid hsl(var(--border));
transition: background-color 0.2s ease;
height: 2.5rem;
padding-left: 1rem;
padding-right: 1rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
max-width: 20rem;
}

.Button:hover {
cursor: pointer;
background-color: var(--ifm-color-primary-lightest);
transition: background-color 0.2s ease;
}

.Button:focus-visible {
outline: none;
box-shadow: 0 0 0 3px hsl(var(--foreground) / 0.5);
}

.Button:disabled {
pointer-events: none;
opacity: 0.5;
}

.Button:disabled:focus-visible {
outline: none;
box-shadow: none;
}

.Button[data-variant='destructive'] {
background-color: hsl(var(--destructive));
color: hsl(var(--destructive-foreground));
transition: background-color 0.2s ease;
}

.Button[data-variant='destructive']:hover {
background-color: hsl(var(--destructive) / 0.9);
}

.Button[data-variant='outline'] {
border: 1px solid hsl(var(--border));
background-color: hsl(var(--background));
color: hsl(var(--foreground));
transition: background-color 0.2s ease, color 0.2s ease;
}

.Button[data-variant='outline']:hover {
cursor: pointer;
background-color: hsl(var(--foreground) / 0.05);
color: hsl(var(--accent-foreground));
}

.Button[data-variant='secondary'] {
background-color: hsl(var(--secondary));
color: hsl(var(--secondary-foreground));
transition: background-color 0.2s ease;
}

.Button[data-variant='secondary']:hover {
background-color: hsl(var(--secondary) / 0.9);
}

.Button[data-variant='ghost'] {
background-color: transparent;
border: 1px solid transparent;
color: hsl(var(--foreground));
display: flex;
align-items: center;
}

.Button[data-variant='ghost']:hover {
background-color: hsl(var(--foreground) / 0.1);
}

.Button[data-variant='link'] {
color: hsl(var(--foreground));
background-color: transparent;
text-decoration: none;
transition: text-decoration 0.2s ease;
border: none;
padding: 0;
height: max-content;
width: max-content;
}

.Button[data-variant='link']:hover {
color: hsl(var(--primary));
text-decoration-color: hsl(var(--foreground));
}

.Button[data-variant='icon'] {
background-color: hsl(var(--foreground));
border: none;
color: hsl(var(--background));
display: flex;
align-items: center;
justify-content: center;
height: 2rem;
width: 2rem;
padding: 0;
border-radius: 9999px;
}

.Button[data-variant='icon']:hover {
background-color: hsl(var(--accent) / 0.8);
}

/* sizing */
.Button[data-size='sm'] {
height: 2.25rem;
border-radius: 0.375rem;
padding-left: 0.75rem;
padding-right: 0.75rem;
}

.Button[data-size='lg'] {
height: 2.75rem;
border-radius: 0.375rem;
padding-left: 2rem;
padding-right: 2rem;
}

.Button[data-size='icon'] {
height: 2.5rem;
width: 2.5rem;
}
Loading

0 comments on commit c354075

Please sign in to comment.