Skip to content

Commit

Permalink
Merge pull request #254 from lidofinance/refactor/stonks-ui
Browse files Browse the repository at this point in the history
refactor: improve Stonks UX
  • Loading branch information
AnnaSila authored Apr 8, 2024
2 parents 01836f2 + 20a9ee1 commit 0d735e8
Show file tree
Hide file tree
Showing 35 changed files with 574 additions and 303 deletions.
21 changes: 21 additions & 0 deletions modules/blockChain/contractAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,17 @@ export const AtcStethAllowedRecipientsRegistry: ChainAddressMap = {
}

export const Stonks: ChainAddressListMap = {
[CHAINS.Mainnet]: [
'0x3e2D251275A92a8169A3B17A2C49016e2de492a7',
'0xf4F6A03E3dbf0aA22083be80fDD340943d275Ea5',
'0x7C2a1E25cA6D778eCaEBC8549371062487846aAF',
'0x79f5E20996abE9f6a48AF6f9b13f1E55AED6f06D',
'0x8Ba6D367D15Ebc52f3eBBdb4a8710948C0918d42',
'0x281e6BB6F26A94250aCEb24396a8E4190726C97e',
'0x64B6aF9A108dCdF470E48e4c0147127F26221A7C',
'0x278f7B6CBB3Cc37374e6a40bDFEBfff08f65A5C7',
'0x2B5a3944A654439379B206DE999639508bA2e850',
],
[CHAINS.Goerli]: [],
[CHAINS.Holesky]: [
'0x7949418C1C8a45b453114568fD3a5526100Eb0D9',
Expand All @@ -178,3 +189,13 @@ export const Stonks: ChainAddressListMap = {
'0x507D0971ffd5de64Ba1fb30Ee6Bb93376035DD00',
],
}

export const StonksStethAllowedRecipientsRegistry: ChainAddressMap = {
[CHAINS.Mainnet]: '0x1a7cFA9EFB4D5BfFDE87B0FaEb1fC65d653868C0',
[CHAINS.Holesky]: '0x4283839a5a92A3A6ed39E48cAD5e4c180b97800B',
}

export const StonksStablesAllowedRecipientsRegistry: ChainAddressMap = {
[CHAINS.Mainnet]: '0x3f0534CCcFb952470775C516DC2eff8396B8A368',
[CHAINS.Holesky]: '0xDd553C1F88EDCFc2033141Cb908eFf9189988A90',
}
22 changes: 22 additions & 0 deletions modules/blockChain/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,25 @@ export const ContractLegoStablesTopUp = createContractHelpers({
factory: TypeChain.TopUpWithLimitsStablesAbi__factory,
address: EvmAddressesByType[MotionType.LegoStablesTopUp],
})

export const ContractStonksStethAllowedRecipientsRegistry =
createContractHelpers({
factory: TypeChain.RegistryWithLimitsAbi__factory,
address: CONTRACT_ADDRESSES.StonksStethAllowedRecipientsRegistry,
})

export const ContractStonksStablesAllowedRecipientsRegistry =
createContractHelpers({
factory: TypeChain.RegistryWithLimitsAbi__factory,
address: CONTRACT_ADDRESSES.StonksStablesAllowedRecipientsRegistry,
})

export const ContractStonksStethTopUp = createContractHelpers({
factory: TypeChain.TopUpWithLimitsAbi__factory,
address: EvmAddressesByType[MotionType.StonksStethTopUp],
})

export const ContractStonksStablesTopUp = createContractHelpers({
factory: TypeChain.TopUpWithLimitsStablesAbi__factory,
address: EvmAddressesByType[MotionType.StonksStablesTopUp],
})
6 changes: 6 additions & 0 deletions modules/motions/evmAddresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export const EvmAddressesByChain: EvmAddresses = {
'0x7d509BFF310d9460b1F613e4e40d342201a83Ae4',
[MotionType.SDVTNodeOperatorManagerChange]:
'0xE31A0599A6772BCf9b2bFc9e25cf941e793c9a7D',
[MotionType.StonksStethTopUp]: '0x6e04aED774B7c89BB43721AcDD7D03C872a51B69',
[MotionType.StonksStablesTopUp]:
'0x0d2aefA542aFa8d9D1Ec35376068B88042FEF5f6',

// next motion factories are @deprecated
// we are keeping them here to display history data
Expand Down Expand Up @@ -236,6 +239,9 @@ export const EvmAddressesByChain: EvmAddresses = {
[MotionType.RccStablesTopUp]: '0xD497E7e039FeFBc64dBB7b75368afb06D07Bc73F',
[MotionType.PmlStablesTopUp]: '0x5BAE56ECfB616eAbbDB048AC930FA1Db82f18900',
[MotionType.AtcStablesTopUp]: '0xfa54cf78474cD4A7f4408Dd0efA36e44b6269813',
[MotionType.StonksStethTopUp]: '0x1240775f1857fB8317bD9ba63f4A8A6A78D9af06',
[MotionType.StonksStablesTopUp]:
'0x65A9913467A9793Bb23726d72C99A470bb9294Ad',

// next motion factories are @deprecated
// we are keeping them here to display history data
Expand Down
2 changes: 2 additions & 0 deletions modules/motions/hooks/useContractEvmScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ export const EVM_CONTRACTS = {
[MotionType.PmlStethTopUp]: CONTRACTS.ContractPmlStethTopUp,
[MotionType.AtcStethTopUp]: CONTRACTS.ContractAtcStethTopUp,
[MotionType.LegoStablesTopUp]: CONTRACTS.ContractLegoStablesTopUp,
[MotionType.StonksStethTopUp]: CONTRACTS.ContractStonksStethTopUp,
[MotionType.StonksStablesTopUp]: CONTRACTS.ContractStonksStablesTopUp,
} as const

export function useContractEvmScript<T extends MotionType | EvmUnrecognized>(
Expand Down
4 changes: 4 additions & 0 deletions modules/motions/hooks/useEVMScriptDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export function useEVMScriptDecoder() {
abis.RegistryWithLimitsAbi__factory.abi,
[KEYS.AtcStethAllowedRecipientsRegistry]:
abis.RegistryWithLimitsAbi__factory.abi,
[KEYS.StonksStethAllowedRecipientsRegistry]:
abis.RegistryWithLimitsAbi__factory.abi,
[KEYS.StonksStablesAllowedRecipientsRegistry]:
abis.RegistryWithLimitsAbi__factory.abi,
}),
)
}, `evm-script-decoder-${chainId}`)
Expand Down
5 changes: 5 additions & 0 deletions modules/motions/hooks/useRegistryWithLimits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
ContractRccStethAllowedRecipientsRegistry,
ContractPmlStethAllowedRecipientsRegistry,
ContractAtcStethAllowedRecipientsRegistry,
ContractStonksStethAllowedRecipientsRegistry,
ContractStonksStablesAllowedRecipientsRegistry,
} from 'modules/blockChain/contracts'
import { getEventsRecipientAdded } from 'modules/motions/utils'
import { MotionType } from 'modules/motions/types'
Expand Down Expand Up @@ -69,6 +71,9 @@ export const REGISTRY_WITH_LIMITS_BY_MOTION_TYPE = {
[MotionType.PmlStethTopUp]: ContractPmlStethAllowedRecipientsRegistry,
[MotionType.AtcStethTopUp]: ContractAtcStethAllowedRecipientsRegistry,
[MotionType.LegoStablesTopUp]: ContractLegoStablesRegistry,
[MotionType.StonksStethTopUp]: ContractStonksStethAllowedRecipientsRegistry,
[MotionType.StonksStablesTopUp]:
ContractStonksStablesAllowedRecipientsRegistry,
} as const

type HookArgs = {
Expand Down
4 changes: 4 additions & 0 deletions modules/motions/hooks/useTokenByTopUpType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ const TOKEN = {
label: 'stETH',
value: (chainId: CHAINS) => CONTRACT_ADDRESSES.STETH[chainId],
},
[MotionType.StonksStethTopUp]: {
label: 'stETH',
value: (chainId: CHAINS) => CONTRACT_ADDRESSES.STETH[chainId],
},
}

const isTopUpType = (type: unknown): type is keyof typeof TOKEN => {
Expand Down
2 changes: 2 additions & 0 deletions modules/motions/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export const MotionTypeForms = {
PmlStethTopUp: 'PmlStethTopUp',
AtcStethTopUp: 'AtcStethTopUp',
LegoStablesTopUp: 'LegoStablesTopUp',
StonksStethTopUp: 'StonksStethTopUp',
StonksStablesTopUp: 'StonksStablesTopUp',
} as const
// intentionally
// eslint-disable-next-line @typescript-eslint/no-redeclare
Expand Down
12 changes: 12 additions & 0 deletions modules/motions/ui/MotionDescription/MotionDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,18 @@ const MOTION_DESCRIPTIONS = {
registryType={MotionType.LegoStablesTopUp}
/>
),
[MotionType.StonksStablesTopUp]: (props: GenericDescProps) => (
<DescTopUpWithLimitsAndCustomToken
{...props}
registryType={MotionType.StonksStablesTopUp}
/>
),
[MotionType.StonksStethTopUp]: (props: DescWithLimitsProps) => (
<DescTopUpWithLimits
{...props}
registryType={MotionType.StonksStethTopUp}
/>
),
} as const

type Props = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
ContractRccStethTopUp,
ContractPmlStethTopUp,
ContractAtcStethTopUp,
ContractStonksStethTopUp,
} from 'modules/blockChain/contracts'
import { MotionType } from 'modules/motions/types'
import { createMotionFormPart } from './createMotionFormPart'
Expand Down Expand Up @@ -66,6 +67,10 @@ export const TOPUP_WITH_LIMITS_MAP = {
evmContract: ContractAtcStethTopUp,
motionType: MotionType.AtcStethTopUp,
},
[MotionType.StonksStethTopUp]: {
evmContract: ContractStonksStethTopUp,
motionType: MotionType.StonksStethTopUp,
},
}

type Program = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
ContractEvmAtcStablesTopUp,
ContractEvmSandboxStablesTopUp,
ContractLegoStablesTopUp,
ContractStonksStablesTopUp,
} from 'modules/blockChain/contracts'
import { MotionType } from 'modules/motions/types'
import { createMotionFormPart } from './createMotionFormPart'
Expand Down Expand Up @@ -66,6 +67,10 @@ export const TOPUP_WITH_LIMITS_MAP = {
evmContract: ContractLegoStablesTopUp,
motionType: MotionType.LegoStablesTopUp,
},
[MotionType.StonksStablesTopUp]: {
evmContract: ContractStonksStablesTopUp,
motionType: MotionType.StonksStablesTopUp,
},
}

type Program = {
Expand Down
7 changes: 7 additions & 0 deletions modules/motions/ui/MotionFormStartNew/Parts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ export const formParts = {
StartNewTopUpWithLimitsAndCustomToken.formParts({
registryType: MotionTypeForms.LegoStablesTopUp,
}),
[MotionTypeForms.StonksStethTopUp]: StartNewTopUpWithLimits.formParts({
registryType: MotionTypeForms.StonksStethTopUp,
}),
[MotionTypeForms.StonksStablesTopUp]:
StartNewTopUpWithLimitsAndCustomToken.formParts({
registryType: MotionTypeForms.StonksStablesTopUp,
}),
} as const

export type FormData = {
Expand Down
2 changes: 2 additions & 0 deletions modules/motions/utils/getMotionTypeDisplayName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export const MotionTypeDisplayNames: Record<
[MotionType.SandboxStablesAdd]: 'Add sandbox stables recipient',
[MotionType.SandboxStablesRemove]: 'Remove sandbox stables recipient',
[MotionType.LegoDAITopUp]: 'Top up LEGO DAI',
[MotionType.StonksStablesTopUp]: 'Top up stonks stablecoins',
[MotionType.StonksStethTopUp]: 'Top up stonks stETH',
} as const

export function getMotionTypeDisplayName(
Expand Down
4 changes: 2 additions & 2 deletions modules/shared/ui/Common/Text/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ export type TextColor = keyof Theme['colors'] | 'inherit'

type Props = {
size: TextSize
weight: TextWeight
weight?: TextWeight
color?: TextColor
truncateLines?: number
isCentered?: boolean
}

export const Text = styled.div<Props>`
font-size: ${({ size }) => size}px;
font-weight: ${({ weight }) => weight};
font-weight: ${({ weight }) => weight ?? 500};
color: ${({ color = 'text' }) =>
color === 'inherit' ? color : `var(--lido-color-${color})`};
Expand Down
2 changes: 1 addition & 1 deletion modules/stonks/hooks/useOrderData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function useOrderData(orderAddress: string) {
)
const buyAmount = formatValue(formatUnits(buyAmountBn, buyTokenDecimals))

const hasBalance = sellTokenBalance.gt(0)
const hasBalance = sellTokenBalance.gt(10) // Covers steth wei issue

const isRecoverable = isExpired && hasBalance

Expand Down
39 changes: 16 additions & 23 deletions modules/stonks/hooks/useStonksData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,23 @@ import { formatUnits } from 'ethers/lib/utils'
import { useAvailableStonks } from './useAvailableStonks'
import { useRouter } from 'next/router'

type StonksData = {
address: string
tokenFrom: {
label: string
address: string
}
tokenTo: {
label: string
address: string
}
marginInBasisPoints: number
orderDurationInSeconds: number
priceToleranceInBasisPoints: number
currentBalance: string
expectedOutput: number
tokenToDecimals: number
}

const minimalBalance = 10

export function useStonksData() {
const { chainId } = useWeb3()
const router = useRouter()
const stonksAddress = String(router.query.stonksAddress)
const { availableStonks } = useAvailableStonks()
const { availableStonks, initialLoading: isAvailableStonksDataLoading } =
useAvailableStonks()

return useSWR(
const { data: stonksData, initialLoading: isStonksDataLoading } = useSWR(
availableStonks?.length ? `stonks-data-${chainId}-${stonksAddress}` : null,
async () => {
if (!availableStonks?.length) {
return
}

return Promise.all(
const processedStonks = await Promise.all(
availableStonks.map(async stonks => {
const [tokenFrom, tokenTo, orderDurationInSeconds] =
await stonks.contract.getOrderParameters()
Expand Down Expand Up @@ -84,15 +67,25 @@ export function useStonksData() {
priceToleranceInBasisPoints: priceToleranceInBasisPoints.toNumber(),
currentBalance: isEnoughBalance
? formatUnits(currentBalance, tokenFromDecimals)
: 0,
: '0',
expectedOutput: Number(
formatUnits(expectedOutput, tokenToDecimals),
),
tokenToDecimals: tokenToDecimals,
isBalanceZero: !isEnoughBalance,
}
}),
).then(stonks => stonks.filter(Boolean)) as Promise<StonksData[]>
)

return processedStonks.sort(
(a, b) => parseFloat(b.currentBalance) - parseFloat(a.currentBalance),
)
},
{ revalidateOnFocus: false, revalidateOnReconnect: false },
)

return {
stonksData,
isStonksDataLoading: isAvailableStonksDataLoading || isStonksDataLoading,
}
}
18 changes: 18 additions & 0 deletions modules/stonks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,21 @@ export type OrderDetailed = {
sellAmountFulfillment?: string
buyAmountFulfillment?: string
}

export type StonksData = {
address: string
tokenFrom: {
label: string
address: string
}
tokenTo: {
label: string
address: string
}
marginInBasisPoints: number
orderDurationInSeconds: number
priceToleranceInBasisPoints: number
currentBalance: string
expectedOutput: number
tokenToDecimals: number
}
43 changes: 43 additions & 0 deletions modules/stonks/ui/StonksGrid/StonksGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Button } from '@lidofinance/lido-ui'
import { stonksInstance } from 'modules/network/utils/urls'
import { Text } from 'modules/shared/ui/Common/Text'
import { StonksData } from 'modules/stonks/types'
import { formatValue } from 'modules/stonks/utils/formatValue'
import { useRouter } from 'next/router'
import { Card, Grid } from './StonksGridStyle'

type Props = {
stonksData: StonksData[]
}

export function StonksGrid({ stonksData }: Props) {
const router = useRouter()

return (
<Grid>
{stonksData.map(stonks => {
const isZero = parseFloat(stonks.currentBalance) === 0
return (
<Card key={stonks.address}>
<Text size={14} weight={800}>
{stonks.tokenFrom.label}
{'->'}
{stonks.tokenTo.label}
</Text>
<Text size={12} color="textSecondary">
Balance: {formatValue(stonks.currentBalance)}{' '}
{stonks.tokenFrom.label}
</Text>
<Button
size="xs"
variant={isZero ? 'outlined' : 'filled'}
onClick={() => router.push(stonksInstance(stonks.address))}
>
{isZero ? 'Inspect' : 'Create Order'}
</Button>
</Card>
)
})}
</Grid>
)
}
Loading

0 comments on commit 0d735e8

Please sign in to comment.