Skip to content

Commit 91efcb4

Browse files
author
Ralf
authored
v7.9.9
v7.9.9
2 parents 7105713 + f34b38d commit 91efcb4

File tree

19 files changed

+2284
-880
lines changed

19 files changed

+2284
-880
lines changed

package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "kwenta",
3-
"version": "7.9.8",
3+
"version": "7.9.9",
44
"description": "Kwenta",
55
"main": "index.js",
66
"scripts": {
@@ -51,5 +51,16 @@
5151
"lint-staged": "13.2.3",
5252
"prettier": "2.8.8",
5353
"typescript": "5.1.6"
54+
},
55+
"pnpm": {
56+
"overrides": {
57+
"got@<11.8.5": ">=11.8.5",
58+
"@adobe/css-tools@<4.3.1": ">=4.3.1",
59+
"semver@<5.7.2": ">=5.7.2",
60+
"semver@>=7.0.0 <7.5.2": ">=7.5.2",
61+
"get-func-name@<2.0.1": ">=2.0.1",
62+
"zod@<=3.22.2": ">=3.22.3",
63+
"postcss@<8.4.31": ">=8.4.31"
64+
}
5465
}
5566
}

packages/app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@kwenta/app",
3-
"version": "7.9.8",
3+
"version": "7.9.9",
44
"scripts": {
55
"dev": "next",
66
"build": "next build",

packages/app/src/constants/links.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ export const EXTERNAL_LINKS = {
1717
OneInchLink: (from: CurrencyKey, to: CurrencyKey) => `https://1inch.exchange/#/${from}/${to}`,
1818
OptimismTokenBridge: 'https://gateway.optimism.io',
1919
},
20-
Options: {
21-
Trade: 'https://options.kwenta.eth.limo/#/trade',
22-
},
2320
Synthetix: {
2421
Home: 'https://www.synthetix.io',
2522
Litepaper: 'https://docs.synthetix.io/litepaper/',

packages/app/src/constants/routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const ROUTES = {
2626
Stake: normalizeRoute('/dashboard', 'staking', 'tab'),
2727
Rewards: normalizeRoute('/dashboard', 'rewards', 'tab'),
2828
Migrate: normalizeRoute('/dashboard', 'migrate', 'tab'),
29+
Redeem: normalizeRoute('/dashboard', 'redeem', 'tab'),
2930
TradingRewards: formatUrl('/dashboard/staking', { tab: 'trading-rewards' }),
3031
},
3132
Exchange: {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Head from 'next/head'
2+
import { FC, ReactNode } from 'react'
3+
import { useTranslation } from 'react-i18next'
4+
5+
import DashboardLayout from 'sections/dashboard/DashboardLayout'
6+
import RedemptionTab from 'sections/dashboard/RedemptionTab'
7+
8+
type RedemptionComponent = FC & { getLayout: (page: ReactNode) => JSX.Element }
9+
10+
const RedeemPage: RedemptionComponent = () => {
11+
const { t } = useTranslation()
12+
13+
return (
14+
<>
15+
<Head>
16+
<title>{t('dashboard-redeem.page-title')}</title>
17+
</Head>
18+
<RedemptionTab />
19+
</>
20+
)
21+
}
22+
23+
RedeemPage.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout>
24+
25+
export default RedeemPage

packages/app/src/sections/dashboard/DashboardLayout.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { EXTERNAL_LINKS } from 'constants/links'
1010
import ROUTES from 'constants/routes'
1111
import AppLayout from 'sections/shared/Layout/AppLayout'
1212
import { useAppSelector } from 'state/hooks'
13-
import { selectStakingMigrationRequired } from 'state/staking/selectors'
13+
import { selectRedemptionRequired, selectStakingMigrationRequired } from 'state/staking/selectors'
1414
import { selectStartMigration } from 'state/stakingMigration/selectors'
1515
import { LeftSideContent, PageContent } from 'styles/common'
1616

@@ -23,6 +23,7 @@ enum Tab {
2323
Governance = 'governance',
2424
Stake = 'staking',
2525
Migrate = 'migrate',
26+
Redeem = 'redeem',
2627
}
2728

2829
const Tabs = Object.values(Tab)
@@ -32,6 +33,7 @@ const DashboardLayout: FC<{ children?: ReactNode }> = ({ children }) => {
3233
const router = useRouter()
3334
const stakingMigrationRequired = useAppSelector(selectStakingMigrationRequired)
3435
const startMigration = useAppSelector(selectStartMigration)
36+
const redemptionRequired = useAppSelector(selectRedemptionRequired)
3537

3638
const tabQuery = useMemo(() => {
3739
if (router.pathname) {
@@ -79,6 +81,13 @@ const DashboardLayout: FC<{ children?: ReactNode }> = ({ children }) => {
7981
href: ROUTES.Dashboard.Migrate,
8082
hidden: !stakingMigrationRequired && !startMigration,
8183
},
84+
{
85+
name: Tab.Redeem,
86+
label: t('dashboard.tabs.redeem'),
87+
active: activeTab === Tab.Redeem,
88+
href: ROUTES.Dashboard.Redeem,
89+
hidden: !redemptionRequired,
90+
},
8291
{
8392
name: Tab.Governance,
8493
label: t('dashboard.tabs.governance'),
@@ -87,7 +96,7 @@ const DashboardLayout: FC<{ children?: ReactNode }> = ({ children }) => {
8796
external: true,
8897
},
8998
],
90-
[t, activeTab, startMigration, stakingMigrationRequired]
99+
[t, activeTab, startMigration, stakingMigrationRequired, redemptionRequired]
91100
)
92101

93102
const visibleTabs = TABS.filter(({ hidden }) => !hidden)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { useTranslation } from 'react-i18next'
2+
import styled from 'styled-components'
3+
4+
import { FlexDivRowCentered } from 'components/layout/flex'
5+
import { SplitContainer } from 'components/layout/grid'
6+
import { Heading } from 'components/Text'
7+
import media from 'styles/media'
8+
9+
import RedeemInputCard from './Stake/InputCards/RedeempInputCard'
10+
11+
const RedemptionTab = () => {
12+
const { t } = useTranslation()
13+
14+
return (
15+
<Container>
16+
<TitleContainer>
17+
<StyledHeading variant="h4">{t('dashboard.stake.tabs.redeem.title')}</StyledHeading>
18+
</TitleContainer>
19+
<SplitContainer>
20+
<RedeemInputCard
21+
inputLabel={t('dashboard.stake.tabs.stake-table.vkwenta-token')}
22+
isVKwenta
23+
/>
24+
<RedeemInputCard
25+
inputLabel={t('dashboard.stake.tabs.stake-table.vekwenta-token')}
26+
isVKwenta={false}
27+
/>
28+
</SplitContainer>
29+
</Container>
30+
)
31+
}
32+
33+
const StyledHeading = styled(Heading)`
34+
font-weight: 400;
35+
`
36+
37+
const TitleContainer = styled(FlexDivRowCentered)`
38+
margin: 30px 0px;
39+
column-gap: 10%;
40+
`
41+
42+
const Container = styled.div`
43+
${media.lessThan('lg')`
44+
padding: 0px 15px;
45+
`}
46+
margin-top: 20px;
47+
`
48+
49+
export default RedemptionTab
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { truncateNumbers } from '@kwenta/sdk/utils'
2+
import { FC, useCallback, useMemo } from 'react'
3+
import { useTranslation } from 'react-i18next'
4+
import styled from 'styled-components'
5+
6+
import Button from 'components/Button'
7+
import ErrorView from 'components/ErrorView'
8+
import { FlexDivRowCentered } from 'components/layout/flex'
9+
import { StakingCard } from 'sections/dashboard/Stake/card'
10+
import { useAppDispatch, useAppSelector } from 'state/hooks'
11+
import { approveKwentaToken, redeemToken } from 'state/staking/actions'
12+
import {
13+
selectIsVeKwentaTokenApproved,
14+
selectIsVKwentaTokenApproved,
15+
selectVeKwentaBalance,
16+
selectVKwentaBalance,
17+
} from 'state/staking/selectors'
18+
import { selectDisableRedeemEscrowKwenta } from 'state/stakingMigration/selectors'
19+
import { numericValueCSS } from 'styles/common'
20+
21+
type RedeemInputCardProps = {
22+
inputLabel: string
23+
isVKwenta: boolean
24+
}
25+
26+
const RedeemInputCard: FC<RedeemInputCardProps> = ({ inputLabel, isVKwenta }) => {
27+
const { t } = useTranslation()
28+
const dispatch = useAppDispatch()
29+
30+
const vKwentaBalance = useAppSelector(selectVKwentaBalance)
31+
const veKwentaBalance = useAppSelector(selectVeKwentaBalance)
32+
const isVKwentaApproved = useAppSelector(selectIsVKwentaTokenApproved)
33+
const isVeKwentaApproved = useAppSelector(selectIsVeKwentaTokenApproved)
34+
const VeKwentaDisableRedeem = useAppSelector(selectDisableRedeemEscrowKwenta)
35+
36+
const isApproved = useMemo(
37+
() => (isVKwenta ? isVKwentaApproved : isVeKwentaApproved),
38+
[isVKwenta, isVKwentaApproved, isVeKwentaApproved]
39+
)
40+
41+
const balance = useMemo(
42+
() => (isVKwenta ? vKwentaBalance : veKwentaBalance),
43+
[isVKwenta, vKwentaBalance, veKwentaBalance]
44+
)
45+
46+
const DisableRedeem = useMemo(
47+
() => (isVKwenta ? false : VeKwentaDisableRedeem),
48+
[isVKwenta, VeKwentaDisableRedeem]
49+
)
50+
51+
const buttonLabel = useMemo(() => {
52+
return isApproved
53+
? t('dashboard.stake.tabs.stake-table.redeem')
54+
: t('dashboard.stake.tabs.stake-table.approve')
55+
}, [isApproved, t])
56+
57+
const submitRedeem = useCallback(() => {
58+
const token = isVKwenta ? 'vKwenta' : 'veKwenta'
59+
60+
if (!isApproved) {
61+
dispatch(approveKwentaToken(token))
62+
} else {
63+
dispatch(redeemToken(token))
64+
}
65+
}, [dispatch, isApproved, isVKwenta])
66+
67+
return (
68+
<>
69+
<StakingInputCardContainer>
70+
<div>
71+
<StakeInputHeader>
72+
<div>{inputLabel}</div>
73+
<StyledFlexDivRowCentered>
74+
<div>{t('dashboard.stake.tabs.stake-table.balance')}</div>
75+
<div className="max">{truncateNumbers(balance, 4)}</div>
76+
</StyledFlexDivRowCentered>
77+
</StakeInputHeader>
78+
</div>
79+
{DisableRedeem ? (
80+
<ErrorView
81+
messageType="warn"
82+
message={t('dashboard.stake.tabs.redeem.warning')}
83+
containerStyle={{
84+
margin: '0',
85+
marginTop: '25px',
86+
padding: '10px 0',
87+
}}
88+
/>
89+
) : (
90+
<Button
91+
fullWidth
92+
variant="flat"
93+
size="small"
94+
disabled={balance.eq(0) || DisableRedeem}
95+
onClick={submitRedeem}
96+
>
97+
{buttonLabel}
98+
</Button>
99+
)}
100+
</StakingInputCardContainer>
101+
</>
102+
)
103+
}
104+
105+
const StyledFlexDivRowCentered = styled(FlexDivRowCentered)`
106+
column-gap: 5px;
107+
`
108+
109+
const StakingInputCardContainer = styled(StakingCard)`
110+
min-height: 125px;
111+
max-height: 250px;
112+
display: flex;
113+
flex-direction: column;
114+
justify-content: space-between;
115+
`
116+
117+
const StakeInputHeader = styled.div`
118+
display: flex;
119+
justify-content: space-between;
120+
align-items: center;
121+
margin-bottom: 10px;
122+
color: ${(props) => props.theme.colors.selectedTheme.title};
123+
font-size: 14px;
124+
.max {
125+
color: ${(props) => props.theme.colors.selectedTheme.button.text.primary};
126+
${numericValueCSS};
127+
}
128+
`
129+
130+
export default RedeemInputCard

packages/app/src/sections/futures/Trade/ShowPercentage.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ const ShowPercentage: React.FC<ShowPercentageProps> = ({
2323
leverageWei,
2424
sizeWei,
2525
}) => {
26+
const isLoss = useMemo(() => {
27+
if (!targetPrice || !price || !leverageSide) return false
28+
29+
const targetPriceWei = wei(targetPrice)
30+
const positiveDiff = targetPriceWei.gt(price)
31+
32+
return leverageSide === 'short' ? positiveDiff : !positiveDiff
33+
}, [price, targetPrice, leverageSide])
34+
2635
const [calculatePercentage, calculatePL] = useMemo(() => {
2736
if (!targetPrice || !price || !leverageSide || !sizeWei) return ''
2837
const priceWei = wei(targetPrice)
@@ -39,21 +48,24 @@ const ShowPercentage: React.FC<ShowPercentageProps> = ({
3948
const percentage = diff.div(price).mul(leverageWei)
4049
const profitLoss = sizeWei.mul(percentage.div(leverageWei)).mul(isStopLoss ? -1 : 1)
4150

42-
return [formatPercent(percentage), formatDollars(profitLoss, { sign: isStopLoss ? '' : '+' })]
51+
return [
52+
formatPercent(percentage),
53+
formatDollars(profitLoss, { sign: profitLoss.lt(0) ? '' : '+' }),
54+
]
4355
}, [price, isStopLoss, leverageSide, leverageWei, targetPrice, sizeWei])
4456

4557
return (
4658
<Body size="large" mono>
47-
<ProfitLoss isStopLoss={isStopLoss}>{calculatePL}</ProfitLoss>
59+
<ProfitLoss isLoss={isLoss}>{calculatePL}</ProfitLoss>
4860
{calculatePercentage}
4961
</Body>
5062
)
5163
}
5264

53-
const ProfitLoss = styled.span<{ isStopLoss: boolean }>`
65+
const ProfitLoss = styled.span<{ isLoss: boolean }>`
5466
margin-right: 0.7rem;
55-
color: ${({ theme, isStopLoss }) =>
56-
isStopLoss
67+
color: ${({ theme, isLoss }) =>
68+
isLoss
5769
? theme.colors.selectedTheme.newTheme.text.negative
5870
: theme.colors.selectedTheme.newTheme.text.positive};
5971
`

packages/app/src/sections/shared/Layout/AppLayout/Header/constants.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,6 @@ export const getMenuLinks = (isMobile: boolean): MenuLinks => [
107107
i18nLabel: 'header.nav.referrals',
108108
link: ROUTES.Referrals.Home,
109109
},
110-
{
111-
i18nLabel: 'header.nav.options.title',
112-
link: EXTERNAL_LINKS.Options.Trade,
113-
},
114110
]
115111

116112
export const DESKTOP_NAV_LINKS = getMenuLinks(false).filter((m) => !m.hidden)

0 commit comments

Comments
 (0)