Skip to content

Commit 992fe67

Browse files
committed
feat: add base Boost tab
1 parent 58ce79f commit 992fe67

16 files changed

Lines changed: 804 additions & 6 deletions

File tree

apps/evm/src/components/Slider/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface SliderProps {
99
min?: number;
1010
disabled?: boolean;
1111
className?: string;
12+
rangeClassName?: string;
1213
}
1314

1415
export const Slider: React.FC<SliderProps> = ({
@@ -19,6 +20,7 @@ export const Slider: React.FC<SliderProps> = ({
1920
step,
2021
disabled = false,
2122
className,
23+
rangeClassName,
2224
}) => (
2325
<SliderPrimitive.Root
2426
data-slot="slider"
@@ -35,7 +37,10 @@ export const Slider: React.FC<SliderProps> = ({
3537
data-slot="slider-track"
3638
className="bg-lightGrey relative grow overflow-hidden rounded-full h-2 w-full"
3739
>
38-
<SliderPrimitive.Range data-slot="slider-range" className="bg-blue absolute h-full" />
40+
<SliderPrimitive.Range
41+
data-slot="slider-range"
42+
className={cn('bg-blue absolute h-full', rangeClassName)}
43+
/>
3944
</SliderPrimitive.Track>
4045

4146
<SliderPrimitive.Thumb

apps/evm/src/containers/AssetAccessor/DisabledActionNotice/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const DisabledActionNotice: React.FC<DisabledActionNoticeProps> = ({ token, acti
2626
return t('assetAccessor.disabledActionNotice.borrow');
2727
}
2828

29+
if (action === 'boost') {
30+
return t('assetAccessor.disabledActionNotice.boost');
31+
}
32+
2933
if (action === 'repay') {
3034
return t('assetAccessor.disabledActionNotice.repay');
3135
}

apps/evm/src/containers/AssetAccessor/index.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@ const AssetAccessor: React.FC<AssetAccessorProps> = ({
3636
return <Spinner />;
3737
}
3838

39-
if (asset.disabledTokenActions.includes(action) || (action === 'borrow' && !asset.isBorrowable)) {
39+
if (
40+
asset.disabledTokenActions.includes(action) ||
41+
((action === 'borrow' || action === 'boost') && !asset.isBorrowable)
42+
) {
4043
return <DisabledActionNotice token={vToken.underlyingToken} action={action} />;
4144
}
4245

43-
if (action === 'borrow' && !asset.isBorrowableByUser) {
46+
if ((action === 'borrow' || action === 'boost') && !asset.isBorrowableByUser) {
4447
return (
4548
<NoticeWarning
4649
description={

apps/evm/src/hooks/useIsFeatureEnabled/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ export const featureFlags = {
142142
],
143143
transactionHistory: [ChainId.BSC_MAINNET, ChainId.ETHEREUM, ChainId.UNICHAIN_MAINNET],
144144
leveragedPositions: [
145+
// DEV ONLY
146+
ChainId.BSC_MAINNET,
147+
// END DEV ONLY
145148
ChainId.BSC_TESTNET,
146149
ChainId.OPBNB_TESTNET,
147150
ChainId.SEPOLIA,

apps/evm/src/libs/tokens/infos/disabledTokenActions/bscMainnet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export const disabledTokenActions: DisabledTokenAction[] = [
55
// BNB
66
{
77
address: NATIVE_TOKEN_ADDRESS,
8-
disabledActions: ['swapAndSupply'],
8+
disabledActions: ['swapAndSupply', 'boost'],
99
},
1010
// vAAVE - Core pool
1111
{
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Icon } from 'components';
2+
import { useTranslation } from 'libs/translations';
3+
4+
export interface FlowProps {
5+
borrowedTokenSymbol: string;
6+
suppliedTokenSymbol: string;
7+
}
8+
9+
// TODO: add tests
10+
11+
export const Flow: React.FC<FlowProps> = ({ borrowedTokenSymbol, suppliedTokenSymbol }) => {
12+
const { t } = useTranslation();
13+
14+
return (
15+
<div className="flex items-center gap-x-[2px] text-grey text-sm">
16+
<p>
17+
{t('operationForm.boost.flows.borrow', {
18+
tokenSymbol: borrowedTokenSymbol,
19+
})}
20+
</p>
21+
22+
<Icon className="w-5 h-5" name="chevronRight" />
23+
24+
<p>{t('operationForm.boost.flows.swap')}</p>
25+
26+
<Icon className="w-5 h-5" name="chevronRight" />
27+
28+
<p>
29+
{t('operationForm.boost.flows.supply', {
30+
tokenSymbol: suppliedTokenSymbol,
31+
})}
32+
</p>
33+
</div>
34+
);
35+
};
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { cn } from '@venusprotocol/ui';
2+
3+
import { Slider } from 'components';
4+
import { useTranslation } from 'libs/translations';
5+
6+
export interface RiskSliderProps {
7+
value: number;
8+
onChange: (newValue: number) => void;
9+
disabled?: boolean;
10+
}
11+
12+
export const RiskSlider: React.FC<RiskSliderProps> = ({ value, onChange, disabled = false }) => {
13+
const { t } = useTranslation();
14+
15+
let rangeClassName = 'bg-green';
16+
17+
if (value > 75) {
18+
rangeClassName = 'bg-red';
19+
} else if (value >= 50) {
20+
rangeClassName = 'bg-orange';
21+
} else if (value >= 25) {
22+
rangeClassName = 'bg-yellow';
23+
}
24+
25+
return (
26+
<div className="space-y-2">
27+
<Slider
28+
disabled={disabled}
29+
value={value}
30+
onChange={onChange}
31+
max={100}
32+
step={1}
33+
rangeClassName={cn('transition-colors', rangeClassName)}
34+
/>
35+
36+
<div className="flex justify-between text-grey text-xs">
37+
<p>{t('operationForm.riskSlider.lowRisk')}</p>
38+
39+
<p>{t('operationForm.riskSlider.highRisk')}</p>
40+
</div>
41+
</div>
42+
);
43+
};
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { useMemo } from 'react';
2+
3+
import { PrimaryButton } from 'components';
4+
import { SwitchChain } from 'containers/SwitchChain';
5+
import { useTranslation } from 'libs/translations';
6+
import type { FormErrorCode } from '../useForm';
7+
8+
export interface SubmitSectionProps {
9+
isFormValid: boolean;
10+
isFormSubmitting: boolean;
11+
formErrorCode?: FormErrorCode;
12+
}
13+
14+
export const SubmitSection: React.FC<SubmitSectionProps> = ({
15+
isFormValid,
16+
isFormSubmitting,
17+
formErrorCode,
18+
}) => {
19+
const { t } = useTranslation();
20+
21+
const submitButtonLabel = useMemo(() => {
22+
if (!isFormValid && formErrorCode !== 'REQUIRES_RISK_ACKNOWLEDGEMENT') {
23+
return t('operationForm.submitButtonLabel.enterValidAmount');
24+
}
25+
26+
return t('operationForm.submitButtonLabel.boost');
27+
}, [isFormValid, t, formErrorCode]);
28+
29+
let dom = (
30+
<PrimaryButton
31+
type="submit"
32+
loading={isFormSubmitting}
33+
disabled={!isFormValid || isFormSubmitting}
34+
className="w-full"
35+
>
36+
{submitButtonLabel}
37+
</PrimaryButton>
38+
);
39+
40+
if (isFormValid) {
41+
dom = <SwitchChain>{dom}</SwitchChain>;
42+
}
43+
44+
return dom;
45+
};
46+
47+
export default SubmitSection;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import BigNumber from 'bignumber.js';
2+
3+
import type { Asset } from 'types';
4+
import { clampToZero } from 'utilities';
5+
6+
export const calculateUserMaxBorrowTokens = ({
7+
borrowedAsset,
8+
suppliedAsset,
9+
userBorrowingPowerCents,
10+
}: { borrowedAsset: Asset; suppliedAsset: Asset; userBorrowingPowerCents: BigNumber }) => {
11+
const userMaxBorrowCents = userBorrowingPowerCents.div(
12+
new BigNumber(1).minus(suppliedAsset.userCollateralFactor),
13+
);
14+
15+
const userMaxBorrowTokens = userMaxBorrowCents
16+
.div(borrowedAsset.tokenPriceCents)
17+
.dp(borrowedAsset.vToken.underlyingToken.decimals);
18+
19+
const marginWithBorrowCapTokens = borrowedAsset.borrowCapTokens.minus(
20+
borrowedAsset.borrowBalanceTokens,
21+
);
22+
23+
// Take borrow cap in consideration
24+
return clampToZero({
25+
value: BigNumber.min(userMaxBorrowTokens, marginWithBorrowCapTokens, borrowedAsset.cashTokens),
26+
});
27+
};

0 commit comments

Comments
 (0)