Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stake update #327

Merged
merged 35 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bb011d6
refactor: wip stake updates
tomjeatt Nov 15, 2024
ab098b4
feature: pass onStakeSuccess
tomjeatt Nov 15, 2024
90ed419
Merge branch 'main' into tom/stake-update
tomjeatt Nov 27, 2024
73c5254
refactor: restructure content
tomjeatt Nov 27, 2024
68ce390
refactor: small screen layout
tomjeatt Nov 27, 2024
a6da1d9
refactor: table layout
tomjeatt Nov 27, 2024
c0e765a
refactor: add manage button
tomjeatt Nov 27, 2024
a7ddbdd
feat: rui approach
danielsimao Nov 28, 2024
2ed5a9d
Merge branch 'main' into tom/stake-update
tomjeatt Dec 3, 2024
e916d84
refactor: format TVL and add translations
tomjeatt Dec 3, 2024
67912cc
refactor: fix sorting and disconnected wallet TVL bug
tomjeatt Dec 3, 2024
afbf8c5
chore: suppress output token
tomjeatt Dec 3, 2024
a10cb4e
chore: sync transaction speed rate type with value
slavastartsev Dec 4, 2024
a89a3a7
chore: smaller files
tomjeatt Dec 4, 2024
d6d30b6
chore: revert style change
tomjeatt Dec 4, 2024
287209c
chore: move modal styles
tomjeatt Dec 4, 2024
c4b010c
Update apps/evm/src/app/[lang]/(bridge)/utils/stakeData.tsx
tomjeatt Dec 4, 2024
e4dd607
Update apps/evm/src/app/[lang]/(bridge)/stake/components/StakeTable/S…
tomjeatt Dec 4, 2024
9510e83
chore: calculate tvl for pell protocol
slavastartsev Dec 4, 2024
4576f1e
fix: runtime error
slavastartsev Dec 4, 2024
ad0b0c5
refactor: code review
tomjeatt Dec 4, 2024
5d34141
chore: merge
tomjeatt Dec 4, 2024
988af90
chore: add non null directive
slavastartsev Dec 4, 2024
9c03166
Merge branch 'main' into tom/stake-update
tomjeatt Dec 4, 2024
8854490
Merge branch 'tom/stake-update' of github.com:bob-collective/ui into …
tomjeatt Dec 4, 2024
fe21b71
chore: remove unused prop
tomjeatt Dec 5, 2024
143db5d
chore: whitespace
tomjeatt Dec 5, 2024
94255cb
refactor: update modal title and use spice name alongside logo
tomjeatt Dec 5, 2024
48cbc5b
refactor: update modal header
tomjeatt Dec 5, 2024
36cf4e0
refactor: style alerts
tomjeatt Dec 5, 2024
70cf690
chore: set fixed chain for staking tvl calculations
slavastartsev Dec 5, 2024
00272f4
refactor: minor layout and styling changes
tomjeatt Dec 5, 2024
71059ca
Merge branch 'tom/stake-update' of github.com:bob-collective/ui into …
tomjeatt Dec 5, 2024
18773b9
chore: show unlinked output tokens
tomjeatt Dec 5, 2024
538e307
chore: truncate URL
tomjeatt Dec 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
552 changes: 552 additions & 0 deletions apps/evm/src/abis/StrategyBaseTVL.abi.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ const GatewayTransactionDetails = ({
<Tooltip isDisabled={!!btcAddress} label={t(i18n)`Connect BTC wallet to access fee rate settings.`}>
<UnstyledButton disabled={!btcAddress} onPress={() => setOpen(true)}>
<Flex alignItems='center' gap='xs'>
<Span size='xs'>{Math.ceil(settings.fee.rate)} sat/vB</Span>
<Span size='xs'>
{Math.ceil(settings.fee.rate)} sat/vB ({settings.fee.selected.speed})
</Span>
<Cog color='grey-50' size='s' />
</Flex>
</UnstyledButton>
Expand Down
64 changes: 8 additions & 56 deletions apps/evm/src/app/[lang]/(bridge)/stake/Stake.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
'use client';

import { Tabs, TabsItem } from '@gobob/ui';
import { Trans } from '@lingui/macro';
import { useStore } from '@tanstack/react-store';
import { watchAccount } from '@wagmi/core';
import { useRouter } from 'next/navigation';
import { Key, useCallback, useEffect, useMemo, useRef } from 'react';
import { useEffect, useRef } from 'react';
import { useConfig } from 'wagmi';
import { Flex } from '@gobob/ui';

import { Layout, TransactionList } from '../components';
import { GetGatewayTransactionsReturnType, useGetGatewayTransactions } from '../hooks';

import { StakingForm } from './components';
import { useGetStakingStrategies } from './hooks';
import { StyledCard, StyledFlex } from './Stake.style';
import { StakeTable } from './components';

import { PageLangParam } from '@/i18n/withLigui';
import { store } from '@/lib/store';
import { GatewayTransactionType, TransactionDirection } from '@/types';

enum Type {
Stake = 'stake',
Unstake = 'unstake'
}
import { GatewayTransactionType } from '@/types';

type Props = PageLangParam & {
searchParams?: { type: Type; 'stake-with': string };
searchParams?: { receive: string };
};

const select = (data: GetGatewayTransactionsReturnType) =>
data.filter((item) => item.subType === GatewayTransactionType.STAKE);

function Stake({ searchParams }: Props) {
const router = useRouter();

const config = useConfig();

const {
Expand All @@ -43,7 +32,6 @@ function Stake({ searchParams }: Props) {
} = useGetGatewayTransactions({
query: { select }
});
const { data: strategies = [] } = useGetStakingStrategies();

const isInitialLoading = useStore(store, (state) => state.bridge.transactions.isInitialLoading);

Expand Down Expand Up @@ -74,48 +62,12 @@ function Stake({ searchParams }: Props) {
}
}, [isInitialLoading, isLoadingTransactions]);

const urlSearchParams = useMemo(() => new URLSearchParams(searchParams), [searchParams]);
const type = (urlSearchParams.get('type') as Type) || Type.Stake;
const direction = type === Type.Stake ? TransactionDirection.L1_TO_L2 : TransactionDirection.L2_TO_L1;

useEffect(() => {
if (!urlSearchParams.get('type') || !urlSearchParams.get('stake-with')) {
urlSearchParams.set('type', type);

router.replace('?' + urlSearchParams);
}
}, [router, type, urlSearchParams]);

const handleChangeTab = useCallback(
(key: Key) => {
urlSearchParams.set('type', key as string);

router.replace('?' + urlSearchParams);
},
[router, urlSearchParams]
);

return (
<Layout>
<StyledFlex alignItems='flex-start' direction={{ base: 'column', md: 'row' }} gap='2xl' marginTop='xl'>
<StyledCard>
<Tabs fullWidth selectedKey={type} size='lg' onSelectionChange={handleChangeTab}>
<TabsItem key={Type.Stake} title={<Trans>Stake</Trans>}>
<></>
</TabsItem>
<TabsItem key={Type.Unstake} title={<Trans>Unstake</Trans>}>
<></>
</TabsItem>
</Tabs>
<StakingForm
key={strategies.length}
direction={direction}
strategies={strategies}
onStakeSuccess={refetchTransactions}
/>
</StyledCard>
<Flex direction='column' gap='xl' marginTop='xl'>
<StakeTable searchParams={searchParams} onStakeSuccess={refetchTransactions} />
<TransactionList data={transactions} isInitialLoading={isInitialLoading} />
</StyledFlex>
</Flex>
</Layout>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,88 +1,33 @@
'use client';

import { PellNetwork } from '@gobob/icons/src/PellNetwork';
import { Avatar, Flex, Input, Item, P, Select, Skeleton, Span, UnstyledButton } from '@gobob/ui';
import { Flex, Input } from '@gobob/ui';
import { t, Trans } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { chain, mergeProps } from '@react-aria/utils';
import { Optional } from '@tanstack/react-query';
import { useRouter, useSearchParams } from 'next/navigation';
import { PropsWithChildren, useEffect, useMemo, useState } from 'react';
import { useAccount } from 'wagmi';

import { BtcTokenInput, GatewayGasSwitch, GatewayTransactionDetails } from '../../../components';
import { useGateway, useGatewayForm } from '../../../hooks';
import { StrategyDetailsModal } from '../StrategyDetailsModal';

import { StrategyData } from './StakeForm';

import { AuthButton } from '@/connect-ui';
import { isProd } from '@/constants';
import { BRIDGE_RECIPIENT, BridgeFormValues } from '@/lib/form/bridge';
import { GatewayTransactionType, InitGatewayTransaction } from '@/types';

const INITIAL_SELECTED_STRATEGY_SLUG = 'solv-solvbtcbbn';

type BtcBridgeFormProps = {
strategies: StrategyData[];
strategy: StrategyData;
onStart: (data: Optional<InitGatewayTransaction, 'amount'>) => void;
onSuccess: (data: InitGatewayTransaction) => void;
onError: () => void;
};

const StrategyOption = ({ children, data }: PropsWithChildren<{ data: StrategyData }>) => {
return (
<Flex alignItems='center' gap='s'>
{data.raw.integration.logo ? (
<Avatar size={children ? '4xl' : '2xl'} src={data.raw.integration.logo} />
) : (
<PellNetwork style={children ? { height: '2rem', width: '2rem' } : { height: '1.3rem', width: '1.3rem' }} />
)}
<P align='start' style={{ color: 'inherit', lineHeight: children ? '1rem' : undefined }}>
{data.raw.integration.name}
{children}
</P>
</Flex>
);
};

const BtcStakeForm = ({ strategies, onStart, onSuccess, onError }: BtcBridgeFormProps): JSX.Element => {
const BtcStakeForm = ({ strategy, onStart, onSuccess, onError }: BtcBridgeFormProps): JSX.Element => {
const { i18n } = useLingui();

const searchParams = useSearchParams();
const router = useRouter();

const { address: evmAddress } = useAccount();

const [isStrategyModalOpen, setStrategyModalOpen] = useState(false);

const [selectedStrategy, setSelectedStrategy] = useState(
searchParams?.get('stake-with') ?? (isProd ? INITIAL_SELECTED_STRATEGY_SLUG : strategies[0]?.raw.integration.slug)
);

const sortedStrategies = useMemo(
() =>
[...strategies].sort((strategyA, strategyB) =>
strategyB.raw.integration.type.localeCompare(strategyA.raw.integration.type)
),
[strategies]
);

const strategy = useMemo(
() => strategies.find((strategy) => strategy.raw.integration.slug === selectedStrategy),
[selectedStrategy, strategies]
);

useEffect(() => {
if (searchParams) {
const urlSearchParams = new URLSearchParams(searchParams);

if (selectedStrategy) urlSearchParams.set('receive', selectedStrategy);
urlSearchParams.set('network', 'bitcoin');
router.replace('?' + urlSearchParams);
}
}, [selectedStrategy, router, searchParams]);

const handleSuccess = () => {
form.resetForm();
};
Expand Down Expand Up @@ -132,55 +77,6 @@ const BtcStakeForm = ({ strategies, onStart, onSuccess, onError }: BtcBridgeForm
onValueChange: gateway.setAmount
})}
/>
<Flex direction='column' gap='xs'>
<Select<StrategyData>
items={sortedStrategies}
label={t(i18n)`Get`}
modalProps={{ title: <Trans>Select Strategy</Trans>, size: 'xs' }}
placeholder={<Skeleton height='3xl' width='11xl' />}
renderValue={({ value }) => (value ? <StrategyOption data={value} /> : undefined)}
size='lg'
type='modal'
{...mergeProps(fields.asset, {
onSelectionChange: setSelectedStrategy
})}
>
{(data) => (
<Item key={data.raw.integration.slug} textValue={data.raw.integration.name}>
<StrategyOption data={data}>
<>
<br />
<Span color='grey-50' size='s' style={{ textTransform: 'capitalize' }}>
{data.raw.integration.type}
</Span>
</>
</StrategyOption>
</Item>
)}
</Select>
{strategy ? (
<>
<UnstyledButton
color='primary-500'
style={{ alignSelf: 'flex-start' }}
onPress={() => setStrategyModalOpen(true)}
>
<Span color='grey-50' size='s' style={{ textDecoration: 'underline' }}>
<Trans>Learn more about {strategy.raw.integration.name}</Trans>
</Span>
</UnstyledButton>
<StrategyDetailsModal
isOpen={isStrategyModalOpen}
strategy={strategy}
onClose={() => setStrategyModalOpen(false)}
/>
</>
) : (
<Span size='s'>
<Skeleton width='60%' />
</Span>
)}
</Flex>
<GatewayGasSwitch
isSelected={gateway.settings.topUp.isEnabled}
onChange={(e) => gateway.settings.topUp.enable(e.target.checked)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Flex } from '@gobob/ui';
import styled from 'styled-components';

const StyledFlex = styled(Flex)`
width: 100%;
`;

export { StyledFlex };
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
'use client';

import { Flex } from '@gobob/ui';
import { useState } from 'react';

import { GatewayTransactionModal } from '../../../components';
import { StrategyData } from '../../hooks';
import { Unstake } from '../Unstake';

import { StyledFlex } from './StakeForm.style';
import { BtcStakeForm } from './BtcStakeForm';

import { InitGatewayTransaction, TransactionDirection } from '@/types';
import { InitGatewayTransaction } from '@/types';

type GatewayTransactionModalState = {
isOpen: boolean;
data?: InitGatewayTransaction;
};

type BridgeFormProps = {
direction: TransactionDirection;
strategies: StrategyData[] | undefined;
strategy: StrategyData;
onStakeSuccess?: () => void;
};

const StakingForm = ({ direction, strategies = [], onStakeSuccess }: BridgeFormProps): JSX.Element => {
const StakingForm = ({ strategy, onStakeSuccess }: BridgeFormProps): JSX.Element => {
const [gatewayModalState, setGatewayModalState] = useState<GatewayTransactionModalState>({
isOpen: false
});
Expand All @@ -42,18 +40,14 @@ const StakingForm = ({ direction, strategies = [], onStakeSuccess }: BridgeFormP

return (
<>
<Flex direction='column' marginTop='2xl'>
{direction === TransactionDirection.L1_TO_L2 ? (
<BtcStakeForm
strategies={strategies}
onError={handleCloseGatewayModal}
onStart={handleStartGateway}
onSuccess={handleGatewaySuccess}
/>
) : (
<Unstake />
)}
</Flex>
<StyledFlex direction='column'>
<BtcStakeForm
strategy={strategy}
onError={handleCloseGatewayModal}
onStart={handleStartGateway}
onSuccess={handleGatewaySuccess}
/>
</StyledFlex>
{gatewayModalState.data && (
<GatewayTransactionModal
data={gatewayModalState.data}
Expand Down
Loading
Loading