From b97db38f17cdd9c8b949f968109c42c71c1fc43a Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 24 May 2024 16:15:00 +0200 Subject: [PATCH 1/2] refactor: improve stonks order card visuals --- modules/stonks/hooks/useOrderData.ts | 5 +- modules/stonks/types.ts | 6 ++ .../ui/StonksOrderCard/StonksOrderCard.tsx | 98 +++++++++++++------ .../StonksOrderCard/StonksOrderCardStyle.tsx | 53 ++++++---- modules/stonks/utils/fetchOffChainOrder.ts | 32 +++++- modules/stonks/utils/getOffChainOrderUrl.ts | 11 +++ 6 files changed, 156 insertions(+), 49 deletions(-) create mode 100644 modules/stonks/utils/getOffChainOrderUrl.ts diff --git a/modules/stonks/hooks/useOrderData.ts b/modules/stonks/hooks/useOrderData.ts index e62fac7b..84911c2c 100644 --- a/modules/stonks/hooks/useOrderData.ts +++ b/modules/stonks/hooks/useOrderData.ts @@ -5,7 +5,7 @@ import { connectContractRpc } from 'modules/motions/utils/connectContractRpc' import { connectERC20Contract } from 'modules/motions/utils/connectTokenContract' import { useSWR } from 'modules/network/hooks/useSwr' import moment from 'moment' -import { OrderDetailed, OrderStatus } from '../types' +import { OrderDetailed, OrderStatus, OrderTransaction } from '../types' import { fetchOffChainOrder } from '../utils/fetchOffChainOrder' import { formatValue } from '../utils/formatValue' @@ -66,6 +66,7 @@ export function useOrderData(orderAddress: string) { let executedBuyAmount = '0' let sellAmountFulfillment = '0' let buyAmountFulfillment = '0' + let transactions: OrderTransaction[] = [] if (offChainOrder) { executedSellAmount = formatValue( @@ -88,6 +89,7 @@ export function useOrderData(orderAddress: string) { status = offChainOrder.status isCreatable = false + transactions = offChainOrder.transactions } return { @@ -113,6 +115,7 @@ export function useOrderData(orderAddress: string) { creationDate: offChainOrder?.creationDate, sellAmountWei, buyAmountWei, + transactions, } }, { diff --git a/modules/stonks/types.ts b/modules/stonks/types.ts index 417bff50..37a17450 100644 --- a/modules/stonks/types.ts +++ b/modules/stonks/types.ts @@ -26,6 +26,10 @@ type OffChainOrderStatus = export type OrderStatus = 'not-created' | OffChainOrderStatus +export type OrderTransaction = { + txHash: string +} + export type OffChainOrder = { creationDate: string owner: string @@ -39,6 +43,7 @@ export type OffChainOrder = { executedBuyAmount: string receiver: string status: OffChainOrderStatus + transactions: OrderTransaction[] } export type OrderDetailed = { @@ -66,6 +71,7 @@ export type OrderDetailed = { executedBuyAmount?: string sellAmountFulfillment?: string buyAmountFulfillment?: string + transactions?: OrderTransaction[] } export type StonksData = { diff --git a/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx b/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx index ee76c257..539f9d8f 100644 --- a/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx +++ b/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx @@ -1,9 +1,12 @@ -import { ButtonIcon, trimAddress, Copy } from '@lidofinance/lido-ui' -import { useCopyToClipboard } from 'modules/shared/hooks/useCopyToClipboard' +import { getEtherscanLink } from '@lido-sdk/helpers' +import { trimAddress } from '@lidofinance/lido-ui' +import { useWeb3 } from 'modules/blockChain/hooks/useWeb3' import { AddressInlineWithPop } from 'modules/shared/ui/Common/AddressInlineWithPop' import { AddressWithPop } from 'modules/shared/ui/Common/AddressWithPop' +import { Text } from 'modules/shared/ui/Common/Text' import { FormattedDate } from 'modules/shared/ui/Utils/FormattedDate' import { OrderDetailed } from 'modules/stonks/types' +import { getOffChainOrderUrl } from 'modules/stonks/utils/getOffChainOrderUrl' import { getOrderStatusText } from 'modules/stonks/utils/getOrderStatusText' import moment from 'moment' import { StonksOrderCardCreateButton } from './StonksOrderCardCreateButton' @@ -15,6 +18,8 @@ import { StatusLabel, StatusValue, ButtonsRow, + OrderUid, + Link, } from './StonksOrderCardStyle' type Props = { @@ -28,44 +33,42 @@ export function StonksOrderCard({ isDataValidating, onInvalidate, }: Props) { - const handleCopyUid = useCopyToClipboard(order.uid ?? '') + const { chainId } = useWeb3() + const orderLink = getOffChainOrderUrl(order.uid, chainId) return (
- Stonks Order + + Stonks order + + + {order.sellTokenLabel} → {order.buyTokenLabel} +
Status - + {getOrderStatusText(order.status)}
{order.uid ? ( -
Off-Chain UID
-
- {trimAddress(order.uid, 6)} - } - size="xs" - variant="translucent" - children="Copy" - /> -
+
Off-Chain Order
+ {orderLink ? ( + + {trimAddress(order.uid, 6)} + + ) : ( +
{trimAddress(order.uid, 6)}
+ )}
) : null} -
Stonks
+
Stonks Contract
@@ -103,12 +106,51 @@ export function StonksOrderCard({ {order.buyAmountFulfillment}%) - -
Recoverable amount
-
- {order.recoverableAmount} {order.sellTokenLabel} -
-
+ + {order.transactions ? ( + <> + {order.transactions.length > 1 ? ( + <> + +
Swap transactions:
+
+ {order.transactions.map(tx => ( + + {trimAddress(tx.txHash, 12)} + + ))} + + ) : ( + +
Swap transaction hash
{' '} + + {trimAddress(order.transactions[0].txHash, 6)} + +
+ )} + + ) : null} + {order.isRecoverable && ( + +
Recoverable amount
+
+ {order.recoverableAmount} {order.sellTokenLabel} +
+
+ )} {order.isCreatable || order.isRecoverable ? ( {order.isCreatable && ( diff --git a/modules/stonks/ui/StonksOrderCard/StonksOrderCardStyle.tsx b/modules/stonks/ui/StonksOrderCard/StonksOrderCardStyle.tsx index af86cde1..253c1bdd 100644 --- a/modules/stonks/ui/StonksOrderCard/StonksOrderCardStyle.tsx +++ b/modules/stonks/ui/StonksOrderCard/StonksOrderCardStyle.tsx @@ -1,6 +1,7 @@ import { Container, Theme } from '@lidofinance/lido-ui' import { BREAKPOINT_MOBILE } from 'modules/globalStyles' import { Text } from 'modules/shared/ui/Common/Text' +import { OrderStatus } from 'modules/stonks/types' import styled, { css } from 'styled-components' export const ContentContainer = styled(Container).attrs({ @@ -34,16 +35,20 @@ export const Card = styled.div` } ` -export const OrderTitle = styled(Text).attrs({ - size: 16, - weight: 800, -})` +export const OrderTitle = styled.div` margin-bottom: 20px; display: flex; align-items: center; justify-content: space-between; ` +export const OrderUid = styled(Text).attrs({ + size: 14, + weight: 800, +})` + color: rgba(39, 56, 82, 0.6); +` + export const StatusLabel = styled(Text).attrs({ size: 12, weight: 500, @@ -53,9 +58,9 @@ export const StatusLabel = styled(Text).attrs({ ` type StatusValueProps = { - isActive: boolean - isCancelled: boolean + value: OrderStatus } + export const StatusValue = styled(Text).attrs({ size: 14, weight: 800, @@ -63,17 +68,24 @@ export const StatusValue = styled(Text).attrs({ text-transform: uppercase; letter-spacing: 0.4px; - ${({ isActive, theme }: StatusValueProps & { theme: Theme }) => - isActive && - css` - color: ${theme.colors.primary}; - `} - - ${({ isCancelled }: StatusValueProps) => - isCancelled && - css` - color: #de186b; - `} + ${({ value, theme }: StatusValueProps & { theme: Theme }) => { + switch (value) { + case 'fulfilled': + return css` + color: #53ba95; + ` + case 'cancelled': + case 'expired': + return css` + color: #de186b; + ` + // rgb(0, 163, 255) - link + default: + return css` + color: ${theme.colors.primary}; + ` + } + }} ` export const Row = styled(Text).attrs({ @@ -83,6 +95,7 @@ export const Row = styled(Text).attrs({ display: flex; justify-content: space-between; align-items: center; + min-height: 32px; & > div { display: flex; @@ -116,3 +129,9 @@ export const MessageBox = styled.div` background-color: rgba(255, 255, 255, 0.4); border-radius: ${({ theme }) => theme.borderRadiusesMap.md + 'px'}; ` + +export const Link = styled.a` + color: ${({ theme }) => theme.colors.primary}; + text-decoration: none; + font-weight: 600; +` diff --git a/modules/stonks/utils/fetchOffChainOrder.ts b/modules/stonks/utils/fetchOffChainOrder.ts index 903e516a..7517dde0 100644 --- a/modules/stonks/utils/fetchOffChainOrder.ts +++ b/modules/stonks/utils/fetchOffChainOrder.ts @@ -1,16 +1,38 @@ import { CHAINS } from '@lido-sdk/constants' import { fetcherStandard } from 'modules/network/utils/fetcherStandard' -import { OffChainOrder } from '../types' +import { OffChainOrder, OrderTransaction } from '../types' const COWSWAP_API_ENDPOINTS: Partial> = { [CHAINS.Mainnet]: 'https://api.cow.fi/mainnet/api/v1', [CHAINS.Goerli]: 'https://api.cow.fi/goerli/api/v1', } +const fetchOrderTransactions = async (orderUid: string, chainId: CHAINS) => { + try { + const cowApiEndpoint = COWSWAP_API_ENDPOINTS[chainId] + + if (!cowApiEndpoint) { + throw new Error(`NO_CHAIN_${chainId}`) + } + + const orderTransactions = await fetcherStandard< + OrderTransaction[] | undefined + >(`${cowApiEndpoint}/trades?orderUid=${orderUid}`) + + if (!orderTransactions?.length) { + return [] + } + + return orderTransactions + } catch (error) { + return [] + } +} + export const fetchOffChainOrder = async ( orderAddress: string, chainId: CHAINS, -) => { +): Promise => { try { const cowApiEndpoint = COWSWAP_API_ENDPOINTS[chainId] @@ -26,7 +48,11 @@ export const fetchOffChainOrder = async ( return null } - return ordersByOwner[0] + const order = ordersByOwner[0] + + const transactions = await fetchOrderTransactions(order.uid, chainId) + + return { ...order, transactions } } catch (error) { return null } diff --git a/modules/stonks/utils/getOffChainOrderUrl.ts b/modules/stonks/utils/getOffChainOrderUrl.ts new file mode 100644 index 00000000..cbcf4e9e --- /dev/null +++ b/modules/stonks/utils/getOffChainOrderUrl.ts @@ -0,0 +1,11 @@ +import { CHAINS } from '@lido-sdk/constants' + +export const getOffChainOrderUrl = ( + orderUid: string | undefined, + chainId: CHAINS, +) => { + if (!orderUid || chainId !== CHAINS.Mainnet) { + return null + } + return `https://explorer.cow.fi/orders/${orderUid}` +} From 5526d5607643c58f13e870dd67eb198ca1aafe1f Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 28 May 2024 14:24:09 +0200 Subject: [PATCH 2/2] fix: order tx hash --- modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx | 2 +- .../ui/StonksOrderResolverForm/StonksOrderResolverForm.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx b/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx index 539f9d8f..8143fee2 100644 --- a/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx +++ b/modules/stonks/ui/StonksOrderCard/StonksOrderCard.tsx @@ -107,7 +107,7 @@ export function StonksOrderCard({ - {order.transactions ? ( + {order.transactions?.length ? ( <> {order.transactions.length > 1 ? ( <> diff --git a/modules/stonks/ui/StonksOrderResolverForm/StonksOrderResolverForm.tsx b/modules/stonks/ui/StonksOrderResolverForm/StonksOrderResolverForm.tsx index 200e04ca..2a3c4da8 100644 --- a/modules/stonks/ui/StonksOrderResolverForm/StonksOrderResolverForm.tsx +++ b/modules/stonks/ui/StonksOrderResolverForm/StonksOrderResolverForm.tsx @@ -41,8 +41,7 @@ export function StonksOrderResolverForm() { const handleSubmit = async (values: FormData) => { try { - const isAddress = utils.isAddress(values.txHashOrAddress) - if (isAddress) { + if (utils.isAddress(values.txHashOrAddress)) { const orderContract = connectContractRpc( StonksOrderAbi__factory, values.txHashOrAddress,