From 0c923aaa322c5fdea0a5062593093c91c8e38856 Mon Sep 17 00:00:00 2001 From: hetfly Date: Wed, 9 Apr 2025 11:45:09 +0200 Subject: [PATCH 01/24] feat: init recent activity behaviour --- apps/web/src/app/components/Header/index.tsx | 41 ++++++++++++++++++- .../app/components/RecentActivity/index.tsx | 25 +++++++++++ apps/web/src/hooks/useCombinedTransactions.ts | 30 ++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/app/components/RecentActivity/index.tsx create mode 100644 apps/web/src/hooks/useCombinedTransactions.ts diff --git a/apps/web/src/app/components/Header/index.tsx b/apps/web/src/app/components/Header/index.tsx index 6d234e862..a070e7069 100644 --- a/apps/web/src/app/components/Header/index.tsx +++ b/apps/web/src/app/components/Header/index.tsx @@ -19,11 +19,13 @@ import { useWalletModalToggle } from '@/store/application/hooks'; import { useAllTransactions } from '@/store/transactions/hooks'; import { shortenAddress } from '@/utils'; +import { useCombinedTransactions } from '@/hooks/useCombinedTransactions'; import { useSignedInWallets } from '@/hooks/useWallets'; import { xChainMap } from '@balancednetwork/xwagmi'; import { bnJs } from '@balancednetwork/xwagmi'; import { Placement } from '@popperjs/core'; import { UseQueryResult, useQuery } from '@tanstack/react-query'; +import RecentActivity from '../RecentActivity'; import { MouseoverTooltip } from '../Tooltip'; import Wallet from '../Wallet'; import { notificationCSS } from '../Wallet/ICONWallets/utils'; @@ -53,6 +55,16 @@ const WalletButtonWrapper = styled(Box)<{ $hasnotification?: boolean }>` } `; +const RecentActivityButtonWrapper = styled(Box)<{ $hasnotification?: boolean }>` + position: relative; + margin-left: 15px; + ${({ $hasnotification }) => ($hasnotification ? notificationCSS : '')} + &::before, &::after { + left: 7px; + top: 13px; + ${({ theme }) => `background-color: ${theme.colors.bg5}`}; + } +`; export const StyledAddress = styled(Typography)` &:hover { color: #2fccdc; @@ -138,9 +150,11 @@ export default function Header(props: { title?: string; className?: string }) { const { data: claimableICX } = useClaimableICX(); const hasBTCB = useHasBTCB(); + const walletButtonRef = React.useRef(null); const [anchor, setAnchor] = React.useState(null); - const walletButtonRef = React.useRef(null); + const recentActivityButtonRef = React.useRef(null); + const [recentActivityAnchor, setRecentActivityAnchor] = React.useState(null); const toggleWalletMenu = () => { setAnchor(anchor ? null : walletButtonRef.current); @@ -155,6 +169,11 @@ export default function Header(props: { title?: string; className?: string }) { } }; + const toggleRecentActivityMenu = () => { + setRecentActivityAnchor(recentActivityAnchor ? null : recentActivityButtonRef.current); + }; + const closeRecentActivityMenu = useCallback(() => setRecentActivityAnchor(null), []); + return (
@@ -235,6 +254,26 @@ export default function Header(props: { title?: string; className?: string }) { + + + +
+ +
+
+ + + + +
+
+
)} diff --git a/apps/web/src/app/components/RecentActivity/index.tsx b/apps/web/src/app/components/RecentActivity/index.tsx new file mode 100644 index 000000000..ab9da987d --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/index.tsx @@ -0,0 +1,25 @@ +import { useCombinedTransactions } from '@/hooks/useCombinedTransactions'; +import React from 'react'; + +const RecentActivity: React.FC = () => { + const { transactions, isMMTransaction } = useCombinedTransactions(); + + return ( +
+

Recent Activity

+ {transactions.length === 0 ? ( +
No activity to display.
+ ) : ( +
    + {transactions.map(transaction => ( +
  • + {isMMTransaction(transaction) ? 'MM Transaction' : 'X Transaction'} - {transaction.id.slice(0, 6)}... +
  • + ))} +
+ )} +
+ ); +}; + +export default RecentActivity; diff --git a/apps/web/src/hooks/useCombinedTransactions.ts b/apps/web/src/hooks/useCombinedTransactions.ts new file mode 100644 index 000000000..f1512446f --- /dev/null +++ b/apps/web/src/hooks/useCombinedTransactions.ts @@ -0,0 +1,30 @@ +import { MMTransaction, useMMTransactionStore } from '@/store/transactions/useMMTransactionStore'; +import { XTransaction, useXTransactionStore } from '@balancednetwork/xwagmi'; +import { useMemo } from 'react'; + +const isMMTransaction = (transaction: MMTransaction | XTransaction): transaction is MMTransaction => { + return !!(transaction as MMTransaction).orderId; +}; + +const getTransactionTimestamp = (transaction: MMTransaction | XTransaction): number => { + if (isMMTransaction(transaction)) { + // For MM transactions, we'll use the current timestamp as a fallback + return Date.now(); + } + return transaction.createdAt ?? 0; +}; + +export const useCombinedTransactions = () => { + const xTransactions = useXTransactionStore(state => state.getTransactions()); + const mmTransactions = useMMTransactionStore(state => Object.values(state.transactions)); + + const sortedTransactions = useMemo( + () => [...xTransactions, ...mmTransactions].sort((a, b) => getTransactionTimestamp(b) - getTransactionTimestamp(a)), + [xTransactions, mmTransactions], + ); + + return { + transactions: sortedTransactions, + isMMTransaction, + }; +}; From 283768bc3ad0941b3246ead6edb9fa589d48a307 Mon Sep 17 00:00:00 2001 From: hetfly Date: Thu, 10 Apr 2025 11:16:50 +0200 Subject: [PATCH 02/24] feat: update dropdown layout --- .../app/components/RecentActivity/index.tsx | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/web/src/app/components/RecentActivity/index.tsx b/apps/web/src/app/components/RecentActivity/index.tsx index ab9da987d..cca531ade 100644 --- a/apps/web/src/app/components/RecentActivity/index.tsx +++ b/apps/web/src/app/components/RecentActivity/index.tsx @@ -1,24 +1,45 @@ +import { Typography } from '@/app/theme'; import { useCombinedTransactions } from '@/hooks/useCombinedTransactions'; import React from 'react'; +import styled from 'styled-components'; + +const Wrap = styled.div` + padding: 25px 0; + width: 400px; + max-width: calc(100vw - 4px); + + ul { + max-height: 500px; + overflow-y: auto; + list-style: none; + padding: 0 25px; + margin-bottom: 0; + } +`; + +const ListItem = styled.li` +`; const RecentActivity: React.FC = () => { const { transactions, isMMTransaction } = useCombinedTransactions(); return ( -
-

Recent Activity

+ + + Recent Activity + {transactions.length === 0 ? (
No activity to display.
) : (
    {transactions.map(transaction => ( -
  • + {isMMTransaction(transaction) ? 'MM Transaction' : 'X Transaction'} - {transaction.id.slice(0, 6)}... -
  • + ))}
)} -
+ ); }; From 5b88efb689e6252f55fac17374d18992b523e080 Mon Sep 17 00:00:00 2001 From: hetfly Date: Thu, 10 Apr 2025 15:05:41 +0200 Subject: [PATCH 03/24] feat: add activity icon --- apps/web/src/app/components/Header/index.tsx | 4 ++-- apps/web/src/assets/icons/activity.svg | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/assets/icons/activity.svg diff --git a/apps/web/src/app/components/Header/index.tsx b/apps/web/src/app/components/Header/index.tsx index a070e7069..058b4f4f8 100644 --- a/apps/web/src/app/components/Header/index.tsx +++ b/apps/web/src/app/components/Header/index.tsx @@ -13,13 +13,13 @@ import { Button, IconButton } from '@/app/components/Button'; import Logo from '@/app/components/Logo'; import { DropdownPopper } from '@/app/components/Popover'; import { Typography } from '@/app/theme'; +import RecentActivityIcon from '@/assets/icons/activity.svg'; import CopyIcon from '@/assets/icons/copy.svg'; import WalletIcon from '@/assets/icons/wallet.svg'; import { useWalletModalToggle } from '@/store/application/hooks'; import { useAllTransactions } from '@/store/transactions/hooks'; import { shortenAddress } from '@/utils'; -import { useCombinedTransactions } from '@/hooks/useCombinedTransactions'; import { useSignedInWallets } from '@/hooks/useWallets'; import { xChainMap } from '@balancednetwork/xwagmi'; import { bnJs } from '@balancednetwork/xwagmi'; @@ -259,7 +259,7 @@ export default function Header(props: { title?: string; className?: string }) {
-
+
+ + + + + + From 2c0031b3bb3eecc311d30017c9dfca0fa5c6f081 Mon Sep 17 00:00:00 2001 From: hetfly Date: Thu, 10 Apr 2025 15:06:11 +0200 Subject: [PATCH 04/24] feat: add history item component --- .../components/RecentActivity/HistoryItem.tsx | 18 ++++++++++++++++++ .../app/components/RecentActivity/index.tsx | 5 ++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/app/components/RecentActivity/HistoryItem.tsx diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx new file mode 100644 index 000000000..a0e6e73b4 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -0,0 +1,18 @@ +import { MMTransaction } from '@/store/transactions/useMMTransactionStore'; +import { XTransaction } from '@balancednetwork/xwagmi'; +import React from 'react'; + +interface HistoryItemProps { + transaction: MMTransaction | XTransaction; + isMMTransaction: (transaction: MMTransaction | XTransaction) => transaction is MMTransaction; +} + +const HistoryItem: React.FC = ({ transaction, isMMTransaction }) => { + return ( +
+ {isMMTransaction(transaction) ? 'MM Transaction' : 'X Transaction'} - {transaction.id.slice(0, 6)}... +
+ ); +}; + +export default HistoryItem; diff --git a/apps/web/src/app/components/RecentActivity/index.tsx b/apps/web/src/app/components/RecentActivity/index.tsx index cca531ade..132febaab 100644 --- a/apps/web/src/app/components/RecentActivity/index.tsx +++ b/apps/web/src/app/components/RecentActivity/index.tsx @@ -2,6 +2,7 @@ import { Typography } from '@/app/theme'; import { useCombinedTransactions } from '@/hooks/useCombinedTransactions'; import React from 'react'; import styled from 'styled-components'; +import HistoryItem from './HistoryItem'; const Wrap = styled.div` padding: 25px 0; @@ -23,6 +24,8 @@ const ListItem = styled.li` const RecentActivity: React.FC = () => { const { transactions, isMMTransaction } = useCombinedTransactions(); + console.log(transactions); + return ( @@ -34,7 +37,7 @@ const RecentActivity: React.FC = () => {
    {transactions.map(transaction => ( - {isMMTransaction(transaction) ? 'MM Transaction' : 'X Transaction'} - {transaction.id.slice(0, 6)}... + ))}
From 15f886c3568ce2bcc75f54849d314115cf36546c Mon Sep 17 00:00:00 2001 From: hetfly Date: Fri, 11 Apr 2025 15:02:04 +0200 Subject: [PATCH 05/24] feat: init history list --- .../components/RecentActivity/HistoryItem.tsx | 25 ++++++--- .../app/components/RecentActivity/index.tsx | 9 ++++ .../transactions/BridgeTransaction.tsx | 49 ++++++++++++++++++ .../transactions/DepositTransaction.tsx | 12 +++++ .../transactions/SwapTransaction.tsx | 51 +++++++++++++++++++ .../transactions/_styledComponents.ts | 43 ++++++++++++++++ apps/web/src/store/user/hooks.tsx | 27 ++++++++++ apps/web/src/utils/index.ts | 26 ++++++++++ 8 files changed, 236 insertions(+), 6 deletions(-) create mode 100644 apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index a0e6e73b4..c1494b0ed 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -1,6 +1,9 @@ import { MMTransaction } from '@/store/transactions/useMMTransactionStore'; -import { XTransaction } from '@balancednetwork/xwagmi'; +import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; import React from 'react'; +import BridgeTransaction from './transactions/BridgeTransaction'; +import DepositTransaction from './transactions/DepositTransaction'; +import SwapTransaction from './transactions/SwapTransaction'; interface HistoryItemProps { transaction: MMTransaction | XTransaction; @@ -8,11 +11,21 @@ interface HistoryItemProps { } const HistoryItem: React.FC = ({ transaction, isMMTransaction }) => { - return ( -
- {isMMTransaction(transaction) ? 'MM Transaction' : 'X Transaction'} - {transaction.id.slice(0, 6)}... -
- ); + if (isMMTransaction(transaction)) { + return
MM Transaction - ID: {transaction.id}
; + } + console.log(transaction.type); + + switch (transaction.type) { + case XTransactionType.SWAP: + return ; + case XTransactionType.BRIDGE: + return ; + case XTransactionType.DEPOSIT: + return ; + default: + return
Unknown Transaction Type - ID: {transaction.id}
; + } }; export default HistoryItem; diff --git a/apps/web/src/app/components/RecentActivity/index.tsx b/apps/web/src/app/components/RecentActivity/index.tsx index 132febaab..19f43be32 100644 --- a/apps/web/src/app/components/RecentActivity/index.tsx +++ b/apps/web/src/app/components/RecentActivity/index.tsx @@ -19,6 +19,15 @@ const Wrap = styled.div` `; const ListItem = styled.li` + padding: 20px 0; + display: block; + border-bottom: 1px solid ${({ theme }) => theme.colors.divider}; + + &:last-child { + border-bottom: none; + margin-bottom: 0; + } + `; const RecentActivity: React.FC = () => { diff --git a/apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx new file mode 100644 index 000000000..3d9de79ea --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx @@ -0,0 +1,49 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance } from '@/utils/formatter'; +import { XTransaction, xChainMap } from '@balancednetwork/xwagmi'; +import React from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import { ElapsedTime } from './_styledComponents'; +import { Amount, Meta, Status } from './_styledComponents'; +import { Container, Details, Title } from './_styledComponents'; + +interface BridgeTransactionProps { + transaction: XTransaction; +} + +const BridgeTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const inputXToken = transaction.input.inputAmount.currency; + const inputAmount = transaction.input.inputAmount.toFixed(); + const destination = transaction.finalDestinationChainId; + const prices = useOraclePrices(); + + const elapsedTime = useElapsedTime(transaction.createdAt); + + return ( + + +
+ Transfer {inputXToken.symbol} + + {formatBalance(inputAmount, prices?.[inputXToken.symbol]?.toFixed() || 1)} {inputXToken.symbol} to{' '} + {xChainMap[destination]?.name} + +
+ + Completed + {formatRelativeTime(elapsedTime)} + +
+ ); +}; + +export default BridgeTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx new file mode 100644 index 000000000..e59edfdc2 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx @@ -0,0 +1,12 @@ +import { XTransaction } from '@balancednetwork/xwagmi'; +import React from 'react'; + +interface DepositTransactionProps { + transaction: XTransaction; +} + +const DepositTransaction: React.FC = ({ transaction }) => { + return
Deposit Transaction
; +}; + +export default DepositTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx new file mode 100644 index 000000000..a09cb6bd9 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx @@ -0,0 +1,51 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance } from '@/utils/formatter'; +import { XTransaction, xMessageActions } from '@balancednetwork/xwagmi'; +import React from 'react'; +import styled, { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import { Amount, Container, Details, ElapsedTime, Meta, Status, Title } from './_styledComponents'; + +interface SwapTransactionProps { + transaction: XTransaction; +} + +const SwapTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const inputXToken = transaction.input.inputAmount.currency; + const outputXToken = transaction.input?.outputAmount!.currency; + const inputAmount = transaction.input.inputAmount.toFixed(); + const outputAmount = transaction.input?.outputAmount!.toFixed(); + const prices = useOraclePrices(); + // const primaryMessage = xMessageActions.getOf(transaction.id, true); + + const elapsedTime = useElapsedTime(transaction.createdAt); + + return ( + + +
+ + Swap {inputXToken.symbol} for {outputXToken.symbol} + + + {formatBalance(inputAmount, prices?.[inputXToken.symbol]?.toFixed() || 1)} {inputXToken.symbol} for{' '} + {formatBalance(outputAmount, prices[outputXToken.address]?.toFixed() || 1)} {outputXToken.symbol} + +
+ + Completed + {formatRelativeTime(elapsedTime)} + +
+ ); +}; + +export default SwapTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts b/apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts new file mode 100644 index 000000000..e2c48fbc0 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts @@ -0,0 +1,43 @@ +import styled from 'styled-components'; + +export const Container = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +export const Details = styled.div` + flex-grow: 1; + margin-left: 12px; +`; + +export const Title = styled.div` + font-weight: bold; + color: #FFFFFF; + font-size: 14px; +`; + +export const Amount = styled.div` + color: ${({ theme }) => theme.colors.text1}; + font-size: 14px; + opacity: 0.9; +`; + +export const Status = styled.div` + color: ${({ theme }) => theme.colors.text}; + font-size: 14px; + text-align: right; +`; + +export const ElapsedTime = styled.div` + color: ${({ theme }) => theme.colors.text1}; + font-size: 14px; + text-align: right; + opacity: 0.9; +`; + +export const Meta = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; +`; diff --git a/apps/web/src/store/user/hooks.tsx b/apps/web/src/store/user/hooks.tsx index 6faf27e2c..1d024e060 100644 --- a/apps/web/src/store/user/hooks.tsx +++ b/apps/web/src/store/user/hooks.tsx @@ -137,3 +137,30 @@ export function useUserLocaleManager(): [SupportedLocale | null, (newLocale: Sup return [locale, setLocale]; } + +import { useEffect, useState } from 'react'; + +export function useElapsedTime(timestamp: number | undefined): number { + const [elapsedTime, setElapsedTime] = useState(0); + + useEffect(() => { + if (timestamp) { + const updateElapsedTime = () => { + setElapsedTime(Math.floor((Date.now() - timestamp) / 1000)); + }; + + updateElapsedTime(); // Update immediately + + const interval = setInterval( + () => { + updateElapsedTime(); + }, + Date.now() - timestamp > 600000 ? 60000 : 1000, + ); // 600000 ms = 10 minutes + + return () => clearInterval(interval); + } + }, [timestamp]); + + return elapsedTime; +} diff --git a/apps/web/src/utils/index.ts b/apps/web/src/utils/index.ts index a8ca2b6b9..55c901ef4 100644 --- a/apps/web/src/utils/index.ts +++ b/apps/web/src/utils/index.ts @@ -464,3 +464,29 @@ export function getTransactionAttributes(xTransactionInput: XTransactionInput) { return { descriptionAction, descriptionAmount }; } + +/** + * Formats a given time difference in seconds into a human-readable relative time string. + * + * @param difference - The difference in seconds between two Unix timestamps. + * @returns A string representing the relative time, such as "x days ago", "x hours ago", "x mins ago", or "just now". + */ +export function formatRelativeTime(difference: number): string { + const secondsInMinute = 60; + const secondsInHour = 3600; + const secondsInDay = 86400; + + const days = Math.floor(difference / secondsInDay); + const hours = Math.floor((difference % secondsInDay) / secondsInHour); + const minutes = Math.floor((difference % secondsInHour) / secondsInMinute); + + if (days > 0) { + return `${days} days ago`; + } else if (hours > 0) { + return `${hours} hours ago`; + } else if (minutes > 0) { + return `${minutes} mins ago`; + } else { + return `just now`; + } +} From 3dfcb7f038fcc468e6019f799c04e7793fbf831f Mon Sep 17 00:00:00 2001 From: hetfly Date: Sun, 13 Apr 2025 12:29:13 +0200 Subject: [PATCH 06/24] feat: init icon only tx history --- .../web/src/app/components/RecentActivity/index.tsx | 4 +++- .../web/src/app/components/home/CollateralPanel.tsx | 4 +++- apps/web/src/hooks/useCombinedTransactions.ts | 5 ++++- apps/web/src/store/transactions/hooks.tsx | 13 +++++++++++++ packages/xwagmi/src/index.ts | 1 + packages/xwagmi/src/xcall/types.ts | 1 + .../src/xcall/zustand/useTransactionStore.tsx | 3 ++- 7 files changed, 27 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/components/RecentActivity/index.tsx b/apps/web/src/app/components/RecentActivity/index.tsx index 19f43be32..63950b021 100644 --- a/apps/web/src/app/components/RecentActivity/index.tsx +++ b/apps/web/src/app/components/RecentActivity/index.tsx @@ -41,7 +41,9 @@ const RecentActivity: React.FC = () => { Recent Activity
{transactions.length === 0 ? ( -
No activity to display.
+ + No activity to display. + ) : (
    {transactions.map(transaction => ( diff --git a/apps/web/src/app/components/home/CollateralPanel.tsx b/apps/web/src/app/components/home/CollateralPanel.tsx index 44958383b..3e33e5294 100644 --- a/apps/web/src/app/components/home/CollateralPanel.tsx +++ b/apps/web/src/app/components/home/CollateralPanel.tsx @@ -41,7 +41,7 @@ import { useHasEnoughICX } from '@/store/wallet/hooks'; import { parseUnits } from '@/utils'; import { formatSymbol, useWrongSymbol } from '@/utils/formatter'; import { showMessageOnBeforeUnload } from '@/utils/messages'; -import { getXChainType } from '@balancednetwork/xwagmi'; +import { XTransactionType, getXChainType } from '@balancednetwork/xwagmi'; import { xChainMap } from '@balancednetwork/xwagmi'; import { useXConnect, useXConnectors } from '@balancednetwork/xwagmi'; import { bnJs } from '@balancednetwork/xwagmi'; @@ -226,6 +226,8 @@ const CollateralPanel = () => { { pending: t`Depositing collateral...`, summary: t`Deposited ${collateralDifference.dp(2).toFormat()} ICX as collateral.`, + //todo + input: undefined, }, ); } else { diff --git a/apps/web/src/hooks/useCombinedTransactions.ts b/apps/web/src/hooks/useCombinedTransactions.ts index f1512446f..c98a4a402 100644 --- a/apps/web/src/hooks/useCombinedTransactions.ts +++ b/apps/web/src/hooks/useCombinedTransactions.ts @@ -1,5 +1,5 @@ import { MMTransaction, useMMTransactionStore } from '@/store/transactions/useMMTransactionStore'; -import { XTransaction, useXTransactionStore } from '@balancednetwork/xwagmi'; +import { XTransaction, useTransactionStore, useXTransactionStore } from '@balancednetwork/xwagmi'; import { useMemo } from 'react'; const isMMTransaction = (transaction: MMTransaction | XTransaction): transaction is MMTransaction => { @@ -17,6 +17,9 @@ const getTransactionTimestamp = (transaction: MMTransaction | XTransaction): num export const useCombinedTransactions = () => { const xTransactions = useXTransactionStore(state => state.getTransactions()); const mmTransactions = useMMTransactionStore(state => Object.values(state.transactions)); + const txses = useTransactionStore(state => state.transactions); + + console.log('txses', txses); const sortedTransactions = useMemo( () => [...xTransactions, ...mmTransactions].sort((a, b) => getTransactionTimestamp(b) - getTransactionTimestamp(a)), diff --git a/apps/web/src/store/transactions/hooks.tsx b/apps/web/src/store/transactions/hooks.tsx index 4c29acecb..56550afec 100644 --- a/apps/web/src/store/transactions/hooks.tsx +++ b/apps/web/src/store/transactions/hooks.tsx @@ -10,6 +10,7 @@ import { toast } from 'react-toastify'; import { NotificationError, NotificationPending } from '@/app/components/Notification/TransactionNotification'; import { getTrackerLink } from '@/utils'; +import { XTransactionInput, transactionActions } from '@balancednetwork/xwagmi'; import { AppDispatch, AppState } from '../index'; import { ICONTxEventLog, addTransaction } from './actions'; import { TransactionDetails } from './reducer'; @@ -26,6 +27,7 @@ export function useTransactionAdder(): ( pending?: string; redirectOnSuccess?: string; isTxSuccessfulBasedOnEvents?: (eventLogs: ICONTxEventLog[]) => boolean; + input?: XTransactionInput; }, ) => void { const { account } = useIconReact(); @@ -41,11 +43,13 @@ export function useTransactionAdder(): ( pending, redirectOnSuccess, isTxSuccessfulBasedOnEvents, + input, }: { summary?: string; pending?: string; redirectOnSuccess?: string; isTxSuccessfulBasedOnEvents?: (eventLogs: ICONTxEventLog[]) => boolean; + input?: XTransactionInput; } = {}, ) => { if (!account) return; @@ -70,6 +74,15 @@ export function useTransactionAdder(): ( toastId: hash, }); + transactionActions.add('0x1.icon', { + hash, + pendingMessage: pending || t`Processing transaction...`, + successMessage: summary, + errorMessage: t`Transaction failed`, + onSuccess: redirectOnSuccess ? () => window.open(redirectOnSuccess, '_blank') : undefined, + input, + }); + dispatch( addTransaction({ hash, from: account, networkId, summary, redirectOnSuccess, isTxSuccessfulBasedOnEvents }), ); diff --git a/packages/xwagmi/src/index.ts b/packages/xwagmi/src/index.ts index 3fd9b6ee4..f3e09c71a 100644 --- a/packages/xwagmi/src/index.ts +++ b/packages/xwagmi/src/index.ts @@ -18,6 +18,7 @@ export * from './hooks'; export * from './useXWagmiStore'; export * from './XWagmiProviders'; export * from './xcall'; +export * from './xcall/zustand/useTransactionStore'; export * from './types'; export type * from './types'; diff --git a/packages/xwagmi/src/xcall/types.ts b/packages/xwagmi/src/xcall/types.ts index 762508acd..dd51cecfe 100644 --- a/packages/xwagmi/src/xcall/types.ts +++ b/packages/xwagmi/src/xcall/types.ts @@ -101,6 +101,7 @@ export type Transaction = { status: TransactionStatus; timestamp: number; + input?: XTransactionInput; pendingMessage?: string; successMessage?: string; errorMessage?: string; diff --git a/packages/xwagmi/src/xcall/zustand/useTransactionStore.tsx b/packages/xwagmi/src/xcall/zustand/useTransactionStore.tsx index 2bb871964..af55aba4a 100644 --- a/packages/xwagmi/src/xcall/zustand/useTransactionStore.tsx +++ b/packages/xwagmi/src/xcall/zustand/useTransactionStore.tsx @@ -12,7 +12,7 @@ import { getXPublicClient } from '@/actions'; // } from '@/app/components/Notification/TransactionNotification'; import { isIconTransaction } from '@/utils'; import { getTrackerLink } from '@/utils'; -import { Transaction, TransactionStatus, XTransactionType } from '@/xcall/types'; +import { Transaction, TransactionStatus, XTransactionInput } from '@/xcall/types'; import { XChainId } from '@balancednetwork/sdk-core'; import { persist } from 'zustand/middleware'; import { xTransactionActions } from './useXTransactionStore'; @@ -47,6 +47,7 @@ export const transactionActions = { pendingMessage?: string; successMessage?: string; errorMessage?: string; + input?: XTransactionInput; onSuccess?: () => void; }, ): Transaction => { From b920b7ba626b9b1ed64c092dca62ee0333ffc559 Mon Sep 17 00:00:00 2001 From: hetfly Date: Sun, 13 Apr 2025 12:29:42 +0200 Subject: [PATCH 07/24] feat: add timestamp to mm transactions --- .../trade/xswap/_components/MMSwapModal.tsx | 1 + .../transactions/useMMTransactionStore.tsx | 33 ++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/web/src/app/pages/trade/xswap/_components/MMSwapModal.tsx b/apps/web/src/app/pages/trade/xswap/_components/MMSwapModal.tsx index 4d11b484e..cc66618bf 100644 --- a/apps/web/src/app/pages/trade/xswap/_components/MMSwapModal.tsx +++ b/apps/web/src/app/pages/trade/xswap/_components/MMSwapModal.tsx @@ -185,6 +185,7 @@ const MMSwapModal = ({ toAmount: trade.outputAmount, orderId: BigInt(intentResult.value.id), taskId: '', + createdAt: Date.now(), }); setIntentId(intentHash.value); diff --git a/apps/web/src/store/transactions/useMMTransactionStore.tsx b/apps/web/src/store/transactions/useMMTransactionStore.tsx index 70b9147e9..b8215f14a 100644 --- a/apps/web/src/store/transactions/useMMTransactionStore.tsx +++ b/apps/web/src/store/transactions/useMMTransactionStore.tsx @@ -25,6 +25,7 @@ export type MMTransaction = { status: MMTransactionStatus; fromAmount: CurrencyAmount; toAmount: CurrencyAmount; + createdAt: number; }; type MMTransactionStore = { @@ -37,6 +38,7 @@ type MMTransactionStore = { setTaskId: (id: string, taskId: string) => void; getPendingTransactions: () => MMTransaction[]; remove: (id: string) => void; + getTransactions: () => MMTransaction[]; }; export const useMMTransactionStore = create()( @@ -50,7 +52,10 @@ export const useMMTransactionStore = create()( add: (transaction: MMTransaction) => { set(state => { - state.transactions[transaction.id] = transaction; + state.transactions[transaction.id] = { + ...transaction, + createdAt: transaction.createdAt || Date.now(), + }; }); }, @@ -79,21 +84,19 @@ export const useMMTransactionStore = create()( }, getPendingTransactions: () => { - return Object.values(get().transactions).filter((transaction: MMTransaction) => { - return ( - transaction.status !== MMTransactionStatus.success - // signedWallets.some(wallet => wallet.xChainId === transaction.sourceChainId) - ); - }); - // .sort((a: MMTransaction, b: MMTransaction) => { - // const aPrimaryMessage = xMessageActions.getOf(a.id, true); - // const bPrimaryMessage = xMessageActions.getOf(b.id, true); - // if (aPrimaryMessage && bPrimaryMessage) { - // return bPrimaryMessage.createdAt - aPrimaryMessage.createdAt; - // } - // return 0; - // }); + return Object.values(get().transactions) + .filter((transaction: MMTransaction) => { + return transaction.status !== MMTransactionStatus.success; + }) + .sort((a: MMTransaction, b: MMTransaction) => b.createdAt - a.createdAt); + }, + + getTransactions: () => { + return Object.values(get().transactions).sort( + (a: MMTransaction, b: MMTransaction) => b.createdAt - a.createdAt, + ); }, + remove: (id: string) => { set(state => { delete state.transactions[id]; From c3a47a59cc8401b85c703b52d682817deb674756 Mon Sep 17 00:00:00 2001 From: hetfly Date: Mon, 14 Apr 2025 06:43:27 +0200 Subject: [PATCH 08/24] chore: update locales --- apps/web/src/locales/en-US.po | 5 +++++ apps/web/src/locales/es-ES.po | 5 +++++ apps/web/src/locales/fr-FR.po | 5 +++++ apps/web/src/locales/ko-KR.po | 5 +++++ apps/web/src/locales/nl-NL.po | 5 +++++ apps/web/src/locales/pl-PL.po | 5 +++++ apps/web/src/locales/pseudo.po | 5 +++++ apps/web/src/locales/vi-VN.po | 5 +++++ 8 files changed, 40 insertions(+) diff --git a/apps/web/src/locales/en-US.po b/apps/web/src/locales/en-US.po index 3182360d4..0e3ca0de3 100644 --- a/apps/web/src/locales/en-US.po +++ b/apps/web/src/locales/en-US.po @@ -1854,6 +1854,7 @@ msgstr "from network fees over the last 30 days." msgid "Approve" msgstr "Approve" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Processing transaction..." @@ -2860,6 +2861,10 @@ msgstr "FEES (24H)" msgid "Switch to" msgstr "Switch to" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "Transaction failed" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "1W" diff --git a/apps/web/src/locales/es-ES.po b/apps/web/src/locales/es-ES.po index 7bed54c44..d19348560 100644 --- a/apps/web/src/locales/es-ES.po +++ b/apps/web/src/locales/es-ES.po @@ -1906,6 +1906,7 @@ msgstr "" msgid "Approve" msgstr "Aceptar" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Procesando la transacción..." @@ -2922,6 +2923,10 @@ msgstr "" msgid "Switch to" msgstr "" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "1S" diff --git a/apps/web/src/locales/fr-FR.po b/apps/web/src/locales/fr-FR.po index b132005b5..a65a978a8 100644 --- a/apps/web/src/locales/fr-FR.po +++ b/apps/web/src/locales/fr-FR.po @@ -1906,6 +1906,7 @@ msgstr "" msgid "Approve" msgstr "Approuver" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Transaction en cours..." @@ -2920,6 +2921,10 @@ msgstr "" msgid "Switch to" msgstr "" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "1S" diff --git a/apps/web/src/locales/ko-KR.po b/apps/web/src/locales/ko-KR.po index 0578449ad..805b26c46 100644 --- a/apps/web/src/locales/ko-KR.po +++ b/apps/web/src/locales/ko-KR.po @@ -1906,6 +1906,7 @@ msgstr "" msgid "Approve" msgstr "승인" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "트랜잭션 처리 중..." @@ -2922,6 +2923,10 @@ msgstr "" msgid "Switch to" msgstr "" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "1주" diff --git a/apps/web/src/locales/nl-NL.po b/apps/web/src/locales/nl-NL.po index 834658fcd..005cc1e5e 100644 --- a/apps/web/src/locales/nl-NL.po +++ b/apps/web/src/locales/nl-NL.po @@ -1906,6 +1906,7 @@ msgstr "" msgid "Approve" msgstr "Goedkeuren" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Transactie verwerken..." @@ -2923,6 +2924,10 @@ msgstr "FEES (24U)" msgid "Switch to" msgstr "" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "1W" diff --git a/apps/web/src/locales/pl-PL.po b/apps/web/src/locales/pl-PL.po index 337b685cf..3391d4795 100644 --- a/apps/web/src/locales/pl-PL.po +++ b/apps/web/src/locales/pl-PL.po @@ -1906,6 +1906,7 @@ msgstr "" msgid "Approve" msgstr "Za" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Przetwarzanie transakcji..." @@ -2922,6 +2923,10 @@ msgstr "" msgid "Switch to" msgstr "" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "1T" diff --git a/apps/web/src/locales/pseudo.po b/apps/web/src/locales/pseudo.po index 92dfbe39e..e6ef3f54d 100644 --- a/apps/web/src/locales/pseudo.po +++ b/apps/web/src/locales/pseudo.po @@ -1850,6 +1850,7 @@ msgstr "" msgid "Approve" msgstr "" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "" @@ -2852,6 +2853,10 @@ msgstr "" msgid "Switch to" msgstr "" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "" diff --git a/apps/web/src/locales/vi-VN.po b/apps/web/src/locales/vi-VN.po index cc27a6361..5c6e4c0af 100644 --- a/apps/web/src/locales/vi-VN.po +++ b/apps/web/src/locales/vi-VN.po @@ -1906,6 +1906,7 @@ msgstr "" msgid "Approve" msgstr "Phê duyệt" +#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Đang xử lý giao dịch..." @@ -2922,6 +2923,10 @@ msgstr "" msgid "Switch to" msgstr "" +#: src/store/transactions/hooks.tsx +msgid "Transaction failed" +msgstr "" + #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" msgstr "1 tuần" From 82adeb50b93c61f648d0b98a59241634e1208101 Mon Sep 17 00:00:00 2001 From: hetfly Date: Mon, 14 Apr 2025 08:39:28 +0200 Subject: [PATCH 09/24] feat: add tx status component --- .../transactions/TransactionStatus.tsx | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx diff --git a/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx b/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx new file mode 100644 index 000000000..cf760a74d --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { Flex } from 'rebass'; +import styled from 'styled-components'; +import { Status } from './_styledComponents'; + +const DotsContainer = styled.div` + display: flex; + align-items: flex-end; + margin-left: 3px; + padding-bottom: 5px; +`; + +const Dot = styled.span` + width: 2px; + height: 2px; + border-radius: 50%; + background-color: currentColor; + margin-right: 3px; + animation: bounce 1.4s infinite ease-in-out; + + &:nth-of-type(1) { + animation-delay: 0s; + } + + &:nth-of-type(2) { + animation-delay: 0.2s; + } + + &:nth-of-type(3) { + animation-delay: 0.4s; + margin-right: 0; + } + + @keyframes bounce { + 0%, 80%, 100% { + transform: scale(0); + } + 40% { + transform: scale(1.0); + } + } +`; + +interface TransactionStatusProps { + status: string; +} + +const TransactionStatus: React.FC = ({ status }) => { + const getStatusText = (status: string) => { + switch (status.toLowerCase()) { + case 'pending': + return ( + + Pending + + + + + + + ); + case 'completed': + return 'Completed'; + case 'failed': + return 'Failed'; + default: + return status; + } + }; + + return {getStatusText(status)}; +}; + +export default TransactionStatus; From 2118b5bd3bd3ffc7dd48688c0a60fc035cddcb62 Mon Sep 17 00:00:00 2001 From: hetfly Date: Mon, 14 Apr 2025 08:39:57 +0200 Subject: [PATCH 10/24] feat: add mm history item --- .../transactions/MMSwapTransaction.tsx | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx diff --git a/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx new file mode 100644 index 000000000..21f1ec7df --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx @@ -0,0 +1,52 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { MMTransaction } from '@/store/transactions/useMMTransactionStore'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance, formatSymbol } from '@/utils/formatter'; +import { XTransaction, xMessageActions } from '@balancednetwork/xwagmi'; +import React from 'react'; +import styled, { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatus from './TransactionStatus'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; + +interface MMSwapTransactionProps { + transaction: MMTransaction; +} + +const MMSwapTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const { fromAmount, toAmount } = transaction; + const prices = useOraclePrices(); + // const primaryMessage = xMessageActions.getOf(transaction.id, true); + + const elapsedTime = useElapsedTime(transaction.createdAt); + + return ( + + +
    + + Swap {formatSymbol(fromAmount.currency.symbol)} for {formatSymbol(toAmount.currency.symbol)} + + + {formatBalance(fromAmount.toFixed(), prices?.[fromAmount.currency.symbol]?.toFixed() || 1)}{' '} + {formatSymbol(fromAmount.currency.symbol)} for{' '} + {formatBalance(toAmount.toFixed(), prices[toAmount.currency.address]?.toFixed() || 1)}{' '} + {formatSymbol(toAmount.currency.symbol)} + +
    + + + {transaction.status === 'pending' ? <>cancel : {formatRelativeTime(elapsedTime)}} + +
    + ); +}; + +export default MMSwapTransaction; From debf89428306f057e44f7bb66840e52c027ece00 Mon Sep 17 00:00:00 2001 From: hetfly Date: Mon, 14 Apr 2025 10:00:35 +0200 Subject: [PATCH 11/24] feat: add possibility to cancel intent from history and remove it from swap panel --- .../components/RecentActivity/HistoryItem.tsx | 43 ++++--- .../transactions/MMSwapTransaction.tsx | 106 +++++++++++++----- .../transactions/TransactionStatus.tsx | 58 +++------- .../trade/xswap/_components/SwapPanel.tsx | 2 - apps/web/src/hooks/useCombinedTransactions.ts | 16 +-- 5 files changed, 132 insertions(+), 93 deletions(-) diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index c1494b0ed..88e4b220b 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -1,8 +1,10 @@ import { MMTransaction } from '@/store/transactions/useMMTransactionStore'; import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import { motion } from 'framer-motion'; import React from 'react'; import BridgeTransaction from './transactions/BridgeTransaction'; import DepositTransaction from './transactions/DepositTransaction'; +import MMSwapTransaction from './transactions/MMSwapTransaction'; import SwapTransaction from './transactions/SwapTransaction'; interface HistoryItemProps { @@ -11,21 +13,34 @@ interface HistoryItemProps { } const HistoryItem: React.FC = ({ transaction, isMMTransaction }) => { - if (isMMTransaction(transaction)) { - return
    MM Transaction - ID: {transaction.id}
    ; - } - console.log(transaction.type); + const renderContent = () => { + console.log('olol', transaction.createdAt); + if (isMMTransaction(transaction)) { + return ; + } - switch (transaction.type) { - case XTransactionType.SWAP: - return ; - case XTransactionType.BRIDGE: - return ; - case XTransactionType.DEPOSIT: - return ; - default: - return
    Unknown Transaction Type - ID: {transaction.id}
    ; - } + switch (transaction.type) { + case XTransactionType.SWAP: + return ; + case XTransactionType.BRIDGE: + return ; + case XTransactionType.DEPOSIT: + return ; + default: + return
    Unknown Transaction Type - ID: {transaction.id}
    ; + } + }; + + return ( + + {renderContent()} + + ); }; export default HistoryItem; diff --git a/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx index 21f1ec7df..506b9b565 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx @@ -1,15 +1,27 @@ +import useIntentProvider from '@/hooks/useIntentProvider'; +import { intentService } from '@/lib/intent'; import { useOraclePrices } from '@/store/oracle/hooks'; -import { MMTransaction } from '@/store/transactions/useMMTransactionStore'; +import { MMTransaction, MMTransactionActions, MMTransactionStatus } from '@/store/transactions/useMMTransactionStore'; import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; import { formatBalance, formatSymbol } from '@/utils/formatter'; import { XTransaction, xMessageActions } from '@balancednetwork/xwagmi'; -import React from 'react'; +import { xChainMap } from '@balancednetwork/xwagmi'; +import React, { useState } from 'react'; +import { Flex } from 'rebass'; import styled, { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; import TransactionStatus from './TransactionStatus'; import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; +enum CancelStatus { + None, + Signing, + AwaitingConfirmation, + Success, + Failed, +} + interface MMSwapTransactionProps { transaction: MMTransaction; } @@ -18,35 +30,79 @@ const MMSwapTransaction: React.FC = ({ transaction }) => const theme = useTheme(); const { fromAmount, toAmount } = transaction; const prices = useOraclePrices(); + const [status, setStatus] = useState(CancelStatus.None); + const { data: intentProvider } = useIntentProvider(transaction.fromAmount.currency.wrapped); // const primaryMessage = xMessageActions.getOf(transaction.id, true); const elapsedTime = useElapsedTime(transaction.createdAt); + const handleCancel = async () => { + setStatus(CancelStatus.Signing); + try { + if (intentProvider) { + const result = await intentService.cancelIntentOrder( + transaction.orderId, + xChainMap[transaction.fromAmount.currency.xChainId].intentChainId, + intentProvider, + ); + + if (result.ok) { + MMTransactionActions.cancel(transaction.id); + setStatus(CancelStatus.Success); + } else { + setStatus(CancelStatus.None); + } + } + } catch (e) { + console.error(e); + setStatus(CancelStatus.None); + } + }; + return ( - - -
    - - Swap {formatSymbol(fromAmount.currency.symbol)} for {formatSymbol(toAmount.currency.symbol)} - - - {formatBalance(fromAmount.toFixed(), prices?.[fromAmount.currency.symbol]?.toFixed() || 1)}{' '} - {formatSymbol(fromAmount.currency.symbol)} for{' '} - {formatBalance(toAmount.toFixed(), prices[toAmount.currency.address]?.toFixed() || 1)}{' '} - {formatSymbol(toAmount.currency.symbol)} - -
    - - - {transaction.status === 'pending' ? <>cancel : {formatRelativeTime(elapsedTime)}} - -
    + <> + + +
    + + Swap {formatSymbol(fromAmount.currency.symbol)} for {formatSymbol(toAmount.currency.symbol)} + + + {formatBalance(fromAmount.toFixed(), prices?.[fromAmount.currency.symbol]?.toFixed() || 1)}{' '} + {formatSymbol(fromAmount.currency.symbol)} for{' '} + {formatBalance(toAmount.toFixed(), prices[toAmount.currency.address]?.toFixed() || 1)}{' '} + {formatSymbol(toAmount.currency.symbol)} + +
    + + + {formatRelativeTime(elapsedTime)} + +
    + {transaction.status === MMTransactionStatus.pending ? ( + + {status === CancelStatus.None && Cancel} + {status === CancelStatus.Signing && Signing...} + {status === CancelStatus.AwaitingConfirmation && Canceling...} + {status === CancelStatus.Success && Done} + + ) : null} + ); }; +const CancelButton = styled.button` + background: none; + border: none; + color: #fb6a6a; + cursor: pointer; + font-size: 14px; + padding: 0; +`; + export default MMSwapTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx b/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx index cf760a74d..87a59af73 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx @@ -1,68 +1,38 @@ import React from 'react'; import { Flex } from 'rebass'; import styled from 'styled-components'; +import Spinner from '../../Spinner'; import { Status } from './_styledComponents'; -const DotsContainer = styled.div` - display: flex; - align-items: flex-end; - margin-left: 3px; - padding-bottom: 5px; -`; - -const Dot = styled.span` - width: 2px; - height: 2px; - border-radius: 50%; - background-color: currentColor; - margin-right: 3px; - animation: bounce 1.4s infinite ease-in-out; - - &:nth-of-type(1) { - animation-delay: 0s; - } - - &:nth-of-type(2) { - animation-delay: 0.2s; - } - - &:nth-of-type(3) { - animation-delay: 0.4s; - margin-right: 0; - } - - @keyframes bounce { - 0%, 80%, 100% { - transform: scale(0); - } - 40% { - transform: scale(1.0); - } - } -`; - interface TransactionStatusProps { status: string; } +const SpinnerWrap = styled.div` + margin-left: 6px; + position: relative; + + top: -4px; +`; + const TransactionStatus: React.FC = ({ status }) => { const getStatusText = (status: string) => { switch (status.toLowerCase()) { case 'pending': return ( - + Pending - - - - - + + + ); case 'completed': return 'Completed'; case 'failed': return 'Failed'; + case 'cancelled': + return 'Cancelled'; default: return status; } diff --git a/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx b/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx index 339c46acf..3e784039d 100644 --- a/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx +++ b/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx @@ -315,8 +315,6 @@ export default function SwapPanel() { handleOutputType('0.002'); }} /> - - diff --git a/apps/web/src/hooks/useCombinedTransactions.ts b/apps/web/src/hooks/useCombinedTransactions.ts index c98a4a402..d87107511 100644 --- a/apps/web/src/hooks/useCombinedTransactions.ts +++ b/apps/web/src/hooks/useCombinedTransactions.ts @@ -1,17 +1,19 @@ import { MMTransaction, useMMTransactionStore } from '@/store/transactions/useMMTransactionStore'; -import { XTransaction, useTransactionStore, useXTransactionStore } from '@balancednetwork/xwagmi'; +import { Transaction, XTransaction, useTransactionStore, useXTransactionStore } from '@balancednetwork/xwagmi'; import { useMemo } from 'react'; const isMMTransaction = (transaction: MMTransaction | XTransaction): transaction is MMTransaction => { return !!(transaction as MMTransaction).orderId; }; -const getTransactionTimestamp = (transaction: MMTransaction | XTransaction): number => { - if (isMMTransaction(transaction)) { - // For MM transactions, we'll use the current timestamp as a fallback - return Date.now(); +const getTransactionTimestamp = (transaction: MMTransaction | XTransaction | Transaction): number => { + if ('createdAt' in transaction) { + return transaction.createdAt ?? 0; } - return transaction.createdAt ?? 0; + if ('timestamp' in transaction) { + return transaction.timestamp ?? 0; + } + return Date.now(); }; export const useCombinedTransactions = () => { @@ -19,8 +21,6 @@ export const useCombinedTransactions = () => { const mmTransactions = useMMTransactionStore(state => Object.values(state.transactions)); const txses = useTransactionStore(state => state.transactions); - console.log('txses', txses); - const sortedTransactions = useMemo( () => [...xTransactions, ...mmTransactions].sort((a, b) => getTransactionTimestamp(b) - getTransactionTimestamp(a)), [xTransactions, mmTransactions], From c6f216825eb919149bdfcfbe5b95cd3b17ff1ea1 Mon Sep 17 00:00:00 2001 From: hetfly Date: Mon, 14 Apr 2025 10:27:44 +0200 Subject: [PATCH 12/24] feat: add recent tx pending hook and animate activity icon --- apps/web/src/app/components/Header/index.tsx | 22 ++++++++++++++-- .../app/components/RecentActivity/index.tsx | 2 -- .../trade/xswap/_components/SwapPanel.tsx | 1 - apps/web/src/hooks/useCombinedTransactions.ts | 26 +++++++++++++++++-- 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/apps/web/src/app/components/Header/index.tsx b/apps/web/src/app/components/Header/index.tsx index 058b4f4f8..af50d4bb4 100644 --- a/apps/web/src/app/components/Header/index.tsx +++ b/apps/web/src/app/components/Header/index.tsx @@ -20,6 +20,7 @@ import { useWalletModalToggle } from '@/store/application/hooks'; import { useAllTransactions } from '@/store/transactions/hooks'; import { shortenAddress } from '@/utils'; +import { useIsAnyTxPending } from '@/hooks/useCombinedTransactions'; import { useSignedInWallets } from '@/hooks/useWallets'; import { xChainMap } from '@balancednetwork/xwagmi'; import { bnJs } from '@balancednetwork/xwagmi'; @@ -65,6 +66,19 @@ const RecentActivityButtonWrapper = styled(Box)<{ $hasnotification?: boolean }>` ${({ theme }) => `background-color: ${theme.colors.bg5}`}; } `; + +const SpinningIcon = styled(RecentActivityIcon)` + @keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(-720deg); + } + } + animation: spin 2s ease-in-out infinite; +`; + export const StyledAddress = styled(Typography)` &:hover { color: #2fccdc; @@ -149,7 +163,7 @@ export default function Header(props: { title?: string; className?: string }) { const wallets = useSignedInWallets(); const { data: claimableICX } = useClaimableICX(); const hasBTCB = useHasBTCB(); - + const isAnyTxPending = useIsAnyTxPending(); const walletButtonRef = React.useRef(null); const [anchor, setAnchor] = React.useState(null); @@ -259,7 +273,11 @@ export default function Header(props: { title?: string; className?: string }) {
    - + {isAnyTxPending ? ( + + ) : ( + + )} { const { transactions, isMMTransaction } = useCombinedTransactions(); - console.log(transactions); - return ( diff --git a/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx b/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx index 3e784039d..d2b91a77e 100644 --- a/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx +++ b/apps/web/src/app/pages/trade/xswap/_components/SwapPanel.tsx @@ -33,7 +33,6 @@ import { formatBalance, formatSymbol } from '@/utils/formatter'; import { XToken, getXChainType } from '@balancednetwork/xwagmi'; import { useXAccount } from '@balancednetwork/xwagmi'; import { XChainId } from '@balancednetwork/xwagmi'; -import MMPendingIntents from './MMPendingIntents'; import MMSwapCommitButton from './MMSwapCommitButton'; import MMSwapInfo from './MMSwapInfo'; import PriceImpact from './PriceImpact'; diff --git a/apps/web/src/hooks/useCombinedTransactions.ts b/apps/web/src/hooks/useCombinedTransactions.ts index d87107511..d71975b2d 100644 --- a/apps/web/src/hooks/useCombinedTransactions.ts +++ b/apps/web/src/hooks/useCombinedTransactions.ts @@ -1,5 +1,11 @@ -import { MMTransaction, useMMTransactionStore } from '@/store/transactions/useMMTransactionStore'; -import { Transaction, XTransaction, useTransactionStore, useXTransactionStore } from '@balancednetwork/xwagmi'; +import { MMTransaction, MMTransactionStatus, useMMTransactionStore } from '@/store/transactions/useMMTransactionStore'; +import { + Transaction, + XTransaction, + XTransactionStatus, + useTransactionStore, + useXTransactionStore, +} from '@balancednetwork/xwagmi'; import { useMemo } from 'react'; const isMMTransaction = (transaction: MMTransaction | XTransaction): transaction is MMTransaction => { @@ -31,3 +37,19 @@ export const useCombinedTransactions = () => { isMMTransaction, }; }; + +export const useIsAnyTxPending = (): boolean => { + const { transactions } = useCombinedTransactions(); + const oneHourAgo = Date.now() - 60 * 60 * 1000; // 1 hour in milliseconds + + return transactions.some(tx => { + const txTimestamp = getTransactionTimestamp(tx); + if (txTimestamp < oneHourAgo) return false; + + if (isMMTransaction(tx)) { + return tx.status === MMTransactionStatus.pending; + } else { + return tx.status === XTransactionStatus.pending; + } + }); +}; From 603b495f80ea543005b6335d5a4fdd6d95b54b00 Mon Sep 17 00:00:00 2001 From: hetfly Date: Wed, 16 Apr 2025 16:52:55 +0200 Subject: [PATCH 13/24] feat: combine icon only and xtransaction in history, update updater --- .../components/RecentActivity/HistoryItem.tsx | 4 +- .../transactions/SwapTransaction.tsx | 74 ++++++-- .../trade/xswap/_components/SwapModal.tsx | 23 ++- apps/web/src/hooks/useCombinedTransactions.ts | 29 +-- apps/web/src/store/transactions/hooks.tsx | 50 +++--- apps/web/src/store/transactions/updater.tsx | 169 +++++++++++------- packages/xwagmi/src/xcall/types.ts | 6 +- packages/xwagmi/src/xcall/utils.ts | 23 +++ .../src/xcall/zustand/useXMessageStore.tsx | 7 +- 9 files changed, 254 insertions(+), 131 deletions(-) diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index 88e4b220b..d79ba0818 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -14,20 +14,20 @@ interface HistoryItemProps { const HistoryItem: React.FC = ({ transaction, isMMTransaction }) => { const renderContent = () => { - console.log('olol', transaction.createdAt); if (isMMTransaction(transaction)) { return ; } switch (transaction.type) { case XTransactionType.SWAP: + case XTransactionType.SWAP_ON_ICON: return ; case XTransactionType.BRIDGE: return ; case XTransactionType.DEPOSIT: return ; default: - return
    Unknown Transaction Type - ID: {transaction.id}
    ; + return
    Unknown Transaction Type - {transaction.type}
    ; } }; diff --git a/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx index a09cb6bd9..baea6cf02 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx @@ -2,26 +2,74 @@ import { useOraclePrices } from '@/store/oracle/hooks'; import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; import { formatBalance } from '@/utils/formatter'; -import { XTransaction, xMessageActions } from '@balancednetwork/xwagmi'; -import React from 'react'; +import { CurrencyAmount, Token } from '@balancednetwork/sdk-core'; +import { Transaction, TransactionStatus as TxStatus, XTransaction, xMessageActions } from '@balancednetwork/xwagmi'; +import React, { useMemo } from 'react'; import styled, { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; -import { Amount, Container, Details, ElapsedTime, Meta, Status, Title } from './_styledComponents'; +import TransactionStatus from './TransactionStatus'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; interface SwapTransactionProps { - transaction: XTransaction; + transaction: XTransaction | Transaction; } const SwapTransaction: React.FC = ({ transaction }) => { const theme = useTheme(); - const inputXToken = transaction.input.inputAmount.currency; - const outputXToken = transaction.input?.outputAmount!.currency; - const inputAmount = transaction.input.inputAmount.toFixed(); - const outputAmount = transaction.input?.outputAmount!.toFixed(); + + // Add null checks for input and its properties + const inputXToken = transaction?.input?.inputAmount?.currency; + const outputXToken = transaction?.input?.outputAmount?.currency; + + // Get raw numeric values + const rawInputAmount = transaction?.input?.inputAmount?.quotient?.toString(); + const rawOutputAmount = transaction?.input?.outputAmount?.quotient?.toString(); + + // Reconstruct CurrencyAmount objects if we have the raw data + const inputAmount = useMemo(() => { + if (!rawInputAmount || !inputXToken) return null; + try { + return CurrencyAmount.fromRawAmount(inputXToken, rawInputAmount); + } catch (e) { + console.error('Failed to reconstruct input amount:', e); + return null; + } + }, [rawInputAmount, inputXToken]); + + const outputAmount = useMemo(() => { + if (!rawOutputAmount || !outputXToken) return null; + try { + return CurrencyAmount.fromRawAmount(outputXToken, rawOutputAmount); + } catch (e) { + console.error('Failed to reconstruct output amount:', e); + return null; + } + }, [rawOutputAmount, outputXToken]); + const prices = useOraclePrices(); // const primaryMessage = xMessageActions.getOf(transaction.id, true); - const elapsedTime = useElapsedTime(transaction.createdAt); + const elapsedTime = useElapsedTime(transaction?.createdAt); + + // Map transaction status to display status + const displayStatus = useMemo(() => { + if (!transaction?.status) return 'pending'; + switch (transaction.status) { + case TxStatus.pending: + return 'pending'; + case TxStatus.success: + return 'completed'; + case TxStatus.failure: + return 'failed'; + default: + return 'pending'; + } + }, [transaction?.status]); + + // Guard against missing data + if (!inputXToken || !outputXToken) { + return null; + } return ( @@ -36,12 +84,14 @@ const SwapTransaction: React.FC = ({ transaction }) => { Swap {inputXToken.symbol} for {outputXToken.symbol} - {formatBalance(inputAmount, prices?.[inputXToken.symbol]?.toFixed() || 1)} {inputXToken.symbol} for{' '} - {formatBalance(outputAmount, prices[outputXToken.address]?.toFixed() || 1)} {outputXToken.symbol} + {inputAmount && formatBalance(inputAmount.toExact(), prices?.[inputXToken.symbol]?.toFixed() || 1)}{' '} + {inputXToken.symbol} for{' '} + {outputAmount && formatBalance(outputAmount.toExact(), prices[outputXToken.address]?.toFixed() || 1)}{' '} + {outputXToken.symbol} - Completed + {formatRelativeTime(elapsedTime)} diff --git a/apps/web/src/app/pages/trade/xswap/_components/SwapModal.tsx b/apps/web/src/app/pages/trade/xswap/_components/SwapModal.tsx index d2eaa8da3..d6bfc8592 100644 --- a/apps/web/src/app/pages/trade/xswap/_components/SwapModal.tsx +++ b/apps/web/src/app/pages/trade/xswap/_components/SwapModal.tsx @@ -19,7 +19,7 @@ import { useTransactionAdder } from '@/store/transactions/hooks'; import { useHasEnoughICX } from '@/store/wallet/hooks'; import { formatBigNumber, shortenAddress, toDec } from '@/utils'; import { formatSymbol } from '@/utils/formatter'; -import { getRlpEncodedSwapData } from '@balancednetwork/xwagmi'; +import { XTransactionType, getICONXTransactionInput, getRlpEncodedSwapData } from '@balancednetwork/xwagmi'; import { bnJs } from '@balancednetwork/xwagmi'; type SwapModalProps = { @@ -70,6 +70,13 @@ const SwapModal = (props: SwapModalProps) => { { pending: message.pendingMessage, summary: message.successMessage, + type: XTransactionType.SWAP_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.SWAP_ON_ICON, + executionTrade.inputAmount, + executionTrade.outputAmount, + ), }, ); handleDismiss(); @@ -97,6 +104,13 @@ const SwapModal = (props: SwapModalProps) => { { pending: message.pendingMessage, summary: message.successMessage, + type: XTransactionType.SWAP_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.SWAP_ON_ICON, + executionTrade.inputAmount, + executionTrade.outputAmount, + ), }, ); handleDismiss(); @@ -128,6 +142,13 @@ const SwapModal = (props: SwapModalProps) => { { pending: message.pendingMessage, summary: message.successMessage, + type: XTransactionType.SWAP_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.SWAP_ON_ICON, + executionTrade.inputAmount, + executionTrade.outputAmount, + ), }, ); handleDismiss(); diff --git a/apps/web/src/hooks/useCombinedTransactions.ts b/apps/web/src/hooks/useCombinedTransactions.ts index d71975b2d..a418b13a7 100644 --- a/apps/web/src/hooks/useCombinedTransactions.ts +++ b/apps/web/src/hooks/useCombinedTransactions.ts @@ -1,34 +1,20 @@ import { MMTransaction, MMTransactionStatus, useMMTransactionStore } from '@/store/transactions/useMMTransactionStore'; -import { - Transaction, - XTransaction, - XTransactionStatus, - useTransactionStore, - useXTransactionStore, -} from '@balancednetwork/xwagmi'; +import { Transaction, XTransaction, XTransactionStatus, useXTransactionStore } from '@balancednetwork/xwagmi'; import { useMemo } from 'react'; const isMMTransaction = (transaction: MMTransaction | XTransaction): transaction is MMTransaction => { return !!(transaction as MMTransaction).orderId; }; -const getTransactionTimestamp = (transaction: MMTransaction | XTransaction | Transaction): number => { - if ('createdAt' in transaction) { - return transaction.createdAt ?? 0; - } - if ('timestamp' in transaction) { - return transaction.timestamp ?? 0; - } - return Date.now(); -}; - -export const useCombinedTransactions = () => { +export const useCombinedTransactions = (): { + transactions: (MMTransaction | XTransaction)[]; + isMMTransaction: (transaction: MMTransaction | XTransaction) => transaction is MMTransaction; +} => { const xTransactions = useXTransactionStore(state => state.getTransactions()); const mmTransactions = useMMTransactionStore(state => Object.values(state.transactions)); - const txses = useTransactionStore(state => state.transactions); const sortedTransactions = useMemo( - () => [...xTransactions, ...mmTransactions].sort((a, b) => getTransactionTimestamp(b) - getTransactionTimestamp(a)), + () => [...xTransactions, ...mmTransactions].sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0)), [xTransactions, mmTransactions], ); @@ -43,8 +29,7 @@ export const useIsAnyTxPending = (): boolean => { const oneHourAgo = Date.now() - 60 * 60 * 1000; // 1 hour in milliseconds return transactions.some(tx => { - const txTimestamp = getTransactionTimestamp(tx); - if (txTimestamp < oneHourAgo) return false; + if (tx.createdAt && tx.createdAt < oneHourAgo) return false; if (isMMTransaction(tx)) { return tx.status === MMTransactionStatus.pending; diff --git a/apps/web/src/store/transactions/hooks.tsx b/apps/web/src/store/transactions/hooks.tsx index 56550afec..e4dbd0909 100644 --- a/apps/web/src/store/transactions/hooks.tsx +++ b/apps/web/src/store/transactions/hooks.tsx @@ -10,7 +10,9 @@ import { toast } from 'react-toastify'; import { NotificationError, NotificationPending } from '@/app/components/Notification/TransactionNotification'; import { getTrackerLink } from '@/utils'; -import { XTransactionInput, transactionActions } from '@balancednetwork/xwagmi'; +import { CurrencyAmount, XChainId } from '@balancednetwork/sdk-core'; +import { XTransactionInput, XTransactionStatus, XTransactionType, xTransactionActions } from '@balancednetwork/xwagmi'; +import { wICX } from '@balancednetwork/xwagmi'; import { AppDispatch, AppState } from '../index'; import { ICONTxEventLog, addTransaction } from './actions'; import { TransactionDetails } from './reducer'; @@ -28,29 +30,17 @@ export function useTransactionAdder(): ( redirectOnSuccess?: string; isTxSuccessfulBasedOnEvents?: (eventLogs: ICONTxEventLog[]) => boolean; input?: XTransactionInput; + type?: XTransactionType; }, ) => void { const { account } = useIconReact(); const networkId = useIconNetworkId(); - const dispatch = useDispatch(); return useCallback( ( response: TransactionResponse, - { - summary, - pending, - redirectOnSuccess, - isTxSuccessfulBasedOnEvents, - input, - }: { - summary?: string; - pending?: string; - redirectOnSuccess?: string; - isTxSuccessfulBasedOnEvents?: (eventLogs: ICONTxEventLog[]) => boolean; - input?: XTransactionInput; - } = {}, + { summary, pending, redirectOnSuccess, isTxSuccessfulBasedOnEvents, input, type } = {}, ) => { if (!account) return; if (!networkId) return; @@ -74,17 +64,29 @@ export function useTransactionAdder(): ( toastId: hash, }); - transactionActions.add('0x1.icon', { - hash, - pendingMessage: pending || t`Processing transaction...`, - successMessage: summary, - errorMessage: t`Transaction failed`, - onSuccess: redirectOnSuccess ? () => window.open(redirectOnSuccess, '_blank') : undefined, - input, - }); + if (type && input) { + xTransactionActions.add({ + id: hash, + type, + status: XTransactionStatus.pending, + secondaryMessageRequired: false, + sourceChainId: '0x1.icon' as XChainId, + finalDestinationChainId: '0x1.icon' as XChainId, + finalDestinationChainInitialBlockHeight: BigInt(0), + createdAt: Date.now(), + input, + }); + } dispatch( - addTransaction({ hash, from: account, networkId, summary, redirectOnSuccess, isTxSuccessfulBasedOnEvents }), + addTransaction({ + hash, + from: account, + networkId, + summary, + redirectOnSuccess, + isTxSuccessfulBasedOnEvents, + }), ); }, [dispatch, networkId, account], diff --git a/apps/web/src/store/transactions/updater.tsx b/apps/web/src/store/transactions/updater.tsx index 077c8dc06..3ce5e73eb 100644 --- a/apps/web/src/store/transactions/updater.tsx +++ b/apps/web/src/store/transactions/updater.tsx @@ -10,6 +10,7 @@ import { toast } from 'react-toastify'; import { NotificationError, NotificationSuccess } from '@/app/components/Notification/TransactionNotification'; import { useBlockNumber } from '@/store/application/hooks'; import { getTrackerLink } from '@/utils'; +import { XChainId, XTransactionType, useXTransactionStore, xTransactionActions } from '@balancednetwork/xwagmi'; import { AppDispatch } from '../index'; import { ICONTxEventLog, finalizeTransaction } from './actions'; @@ -29,78 +30,112 @@ export default function Updater(): null { const dispatch = useDispatch(); const transactions = useAllTransactions(); + const xTransactions = useXTransactionStore(state => + Object.values(state.transactions).filter(tx => tx.type === XTransactionType.SWAP_ON_ICON), + ); // biome-ignore lint/correctness/useExhaustiveDependencies: React.useEffect(() => { - if (!networkId || !iconService || !transactions) return; - - Object.keys(transactions) - .filter(hash => shouldCheck(transactions[hash])) - .forEach(hash => { - iconService - .getTransactionResult(hash) - .execute() - .then(txResult => { - if (txResult) { - dispatch( - finalizeTransaction({ - networkId, - hash, - receipt: { - blockHash: txResult.blockHash, - blockHeight: txResult.blockHeight, - scoreAddress: txResult.scoreAddress, - // from: receipt.from, - status: Converter.toNumber(txResult.status), - to: txResult.to, - txHash: txResult.txHash, - txIndex: txResult.txIndex, - ...(transactions[hash].isTxSuccessfulBasedOnEvents && { - eventLogs: txResult.eventLogs as ICONTxEventLog[], - }), - }, - }), - ); - - const link = getTrackerLink(networkId, hash, 'transaction'); - const toastProps = { - onClick: () => window.open(link, '_blank'), - }; - - const predicate = transactions[hash].isTxSuccessfulBasedOnEvents; - - // success - if ( - (!predicate && txResult.status === 1) || - (predicate && predicate(txResult.eventLogs as ICONTxEventLog[])) - ) { - toast.update(txResult.txHash, { - ...toastProps, - render: ( - - ), - autoClose: 5000, - }); - } - - // failure - if (txResult.status === 0 || (predicate && !predicate(txResult.eventLogs as ICONTxEventLog[]))) { - toast.update(txResult.txHash, { - ...toastProps, - render: , - autoClose: 5000, - }); - } + if (!networkId || !iconService) return; + + // Check regular transactions + if (transactions) { + Object.keys(transactions) + .filter(hash => shouldCheck(transactions[hash])) + .forEach(hash => checkAndUpdateTransaction(hash)); + } + + // Check SWAP_ON_ICON transactions + xTransactions + .filter(tx => tx.status === 'pending') + .forEach(tx => { + const hash = tx.id; + if (hash) { + checkAndUpdateTransaction(hash); + } + }); + }, [networkId, iconService, transactions, xTransactions, dispatch, lastBlockNumber]); + + const checkAndUpdateTransaction = (hash: string) => { + iconService + .getTransactionResult(hash) + .execute() + .then(txResult => { + if (txResult) { + // Update regular transaction if it exists + if (transactions?.[hash]) { + dispatch( + finalizeTransaction({ + networkId, + hash, + receipt: { + blockHash: txResult.blockHash, + blockHeight: txResult.blockHeight, + scoreAddress: txResult.scoreAddress, + status: Converter.toNumber(txResult.status), + to: txResult.to, + txHash: txResult.txHash, + txIndex: txResult.txIndex, + ...(transactions[hash].isTxSuccessfulBasedOnEvents && { + eventLogs: txResult.eventLogs as ICONTxEventLog[], + }), + }, + }), + ); + } + + const link = getTrackerLink(networkId, hash, 'transaction'); + const toastProps = { + onClick: () => window.open(link, '_blank'), + }; + + // Find corresponding xTransaction if it exists + const xTransaction = xTransactions.find(tx => tx.id === hash); + + // success + if (txResult.status === 1) { + // Update xTransaction status if it exists + if (xTransaction) { + xTransactionActions.success(xTransaction.id); + } + + // Show success notification if regular transaction exists + if (transactions?.[hash]) { + toast.update(txResult.txHash, { + ...toastProps, + render: ( + + ), + autoClose: 5000, + }); + } + } + + // failure + if (txResult.status === 0) { + // Update xTransaction status if it exists + if (xTransaction) { + xTransactionActions.fail(xTransaction.id); + } + + // // Show error notification if regular transaction exists + if (transactions?.[hash]) { + toast.update(txResult.txHash, { + ...toastProps, + render: , + autoClose: 5000, + }); } - }) - .catch(error => { - console.error(`failed to check transaction hash: ${hash}`, error); - }); + } + } + }) + .catch(error => { + console.error(`failed to check transaction hash: ${hash}`, error); }); - }, [networkId, iconService, transactions, dispatch, lastBlockNumber]); + }; return null; } diff --git a/packages/xwagmi/src/xcall/types.ts b/packages/xwagmi/src/xcall/types.ts index dd51cecfe..756e5466e 100644 --- a/packages/xwagmi/src/xcall/types.ts +++ b/packages/xwagmi/src/xcall/types.ts @@ -94,7 +94,7 @@ export type XTransactionInput = { withdrawAmountB?: CurrencyAmount; }; -export type Transaction = { +export interface Transaction { id: string; hash: string; xChainId: XChainId; @@ -102,6 +102,7 @@ export type Transaction = { timestamp: number; input?: XTransactionInput; + type?: XTransactionType; pendingMessage?: string; successMessage?: string; errorMessage?: string; @@ -109,7 +110,8 @@ export type Transaction = { onSuccess?: () => void; // Callback on success rawEventLogs?: any[]; -}; + createdAt?: number; +} export type XCallMessageEvent = { eventType: XCallEventType; diff --git a/packages/xwagmi/src/xcall/utils.ts b/packages/xwagmi/src/xcall/utils.ts index e939862ab..5291c358c 100644 --- a/packages/xwagmi/src/xcall/utils.ts +++ b/packages/xwagmi/src/xcall/utils.ts @@ -8,6 +8,7 @@ import { xTokenMap } from '@/constants/xTokens'; import { XChain, XToken } from '@/types'; import { uintToBytes } from '@/utils'; import { xChains } from '../constants/xChains'; +import { XTransactionInput, XTransactionType } from './types'; export function getBytesFromNumber(value) { const hexString = value.toString(16).padStart(2, '0'); @@ -112,3 +113,25 @@ export const getSupportedXChainForSwapToken = (currency?: Currency | XToken | nu return xChains.filter(x => xChainIds.includes(x.xChainId)); }; + +export const getICONXTransactionInput = ( + account: string, + type: XTransactionType, + inputAmount: CurrencyAmount, + outputAmount?: CurrencyAmount, +): XTransactionInput => { + return { + direction: { + from: '0x1.icon', + to: '0x1.icon', + }, + type, + account, + inputAmount: inputAmount, + outputAmount: outputAmount, + xCallFee: { + noRollback: 0n, + rollback: 0n, + }, + }; +}; diff --git a/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx b/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx index e005d5006..5887225d5 100644 --- a/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx +++ b/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx @@ -9,7 +9,7 @@ import { immer } from 'zustand/middleware/immer'; import { getXPublicClient } from '@/actions'; import { xChainMap } from '@/constants/xChains'; import { jsonStorageOptions } from '@/utils/zustand'; -import { XCallEventType, XTransaction } from '../types'; +import { XCallEventType, XTransaction, XTransactionType } from '../types'; import { TransactionStatus, XCallEventMap, XMessage, XMessageStatus } from '../types'; import { transactionActions } from './useTransactionStore'; import { useXCallEventScanner, xCallEventActions } from './useXCallEventStore'; @@ -177,6 +177,11 @@ export const useXMessageStore = create()( const xTransaction = xTransactionActions.get(xMessage.xTransactionId); if (!xTransaction) return; + // Skip XMessage handling for SWAP_ON_ICON transactions + if (xTransaction.type === XTransactionType.SWAP_ON_ICON) { + return; + } + if (xMessage.isPrimary) { if (xMessage.status === XMessageStatus.CALL_EXECUTED) { if (xTransaction.secondaryMessageRequired) { From f89ba17e5c7554ac40d39b77d4014a3aa08e5a37 Mon Sep 17 00:00:00 2001 From: hetfly Date: Wed, 16 Apr 2025 16:54:19 +0200 Subject: [PATCH 14/24] chore: update locales --- apps/web/src/locales/en-US.po | 5 ++--- apps/web/src/locales/es-ES.po | 5 ++--- apps/web/src/locales/fr-FR.po | 5 ++--- apps/web/src/locales/ko-KR.po | 5 ++--- apps/web/src/locales/nl-NL.po | 5 ++--- apps/web/src/locales/pl-PL.po | 5 ++--- apps/web/src/locales/pseudo.po | 5 ++--- apps/web/src/locales/vi-VN.po | 5 ++--- 8 files changed, 16 insertions(+), 24 deletions(-) diff --git a/apps/web/src/locales/en-US.po b/apps/web/src/locales/en-US.po index 0e3ca0de3..140cd6560 100644 --- a/apps/web/src/locales/en-US.po +++ b/apps/web/src/locales/en-US.po @@ -1854,7 +1854,6 @@ msgstr "from network fees over the last 30 days." msgid "Approve" msgstr "Approve" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Processing transaction..." @@ -2862,8 +2861,8 @@ msgid "Switch to" msgstr "Switch to" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "Transaction failed" +#~ msgid "Transaction failed" +#~ msgstr "Transaction failed" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" diff --git a/apps/web/src/locales/es-ES.po b/apps/web/src/locales/es-ES.po index d19348560..7105447d2 100644 --- a/apps/web/src/locales/es-ES.po +++ b/apps/web/src/locales/es-ES.po @@ -1906,7 +1906,6 @@ msgstr "" msgid "Approve" msgstr "Aceptar" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Procesando la transacción..." @@ -2924,8 +2923,8 @@ msgid "Switch to" msgstr "" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "" +#~ msgid "Transaction failed" +#~ msgstr "" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" diff --git a/apps/web/src/locales/fr-FR.po b/apps/web/src/locales/fr-FR.po index a65a978a8..6a75e079f 100644 --- a/apps/web/src/locales/fr-FR.po +++ b/apps/web/src/locales/fr-FR.po @@ -1906,7 +1906,6 @@ msgstr "" msgid "Approve" msgstr "Approuver" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Transaction en cours..." @@ -2922,8 +2921,8 @@ msgid "Switch to" msgstr "" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "" +#~ msgid "Transaction failed" +#~ msgstr "" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" diff --git a/apps/web/src/locales/ko-KR.po b/apps/web/src/locales/ko-KR.po index 805b26c46..154566852 100644 --- a/apps/web/src/locales/ko-KR.po +++ b/apps/web/src/locales/ko-KR.po @@ -1906,7 +1906,6 @@ msgstr "" msgid "Approve" msgstr "승인" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "트랜잭션 처리 중..." @@ -2924,8 +2923,8 @@ msgid "Switch to" msgstr "" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "" +#~ msgid "Transaction failed" +#~ msgstr "" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" diff --git a/apps/web/src/locales/nl-NL.po b/apps/web/src/locales/nl-NL.po index 005cc1e5e..371a57805 100644 --- a/apps/web/src/locales/nl-NL.po +++ b/apps/web/src/locales/nl-NL.po @@ -1906,7 +1906,6 @@ msgstr "" msgid "Approve" msgstr "Goedkeuren" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Transactie verwerken..." @@ -2925,8 +2924,8 @@ msgid "Switch to" msgstr "" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "" +#~ msgid "Transaction failed" +#~ msgstr "" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" diff --git a/apps/web/src/locales/pl-PL.po b/apps/web/src/locales/pl-PL.po index 3391d4795..16394a636 100644 --- a/apps/web/src/locales/pl-PL.po +++ b/apps/web/src/locales/pl-PL.po @@ -1906,7 +1906,6 @@ msgstr "" msgid "Approve" msgstr "Za" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Przetwarzanie transakcji..." @@ -2924,8 +2923,8 @@ msgid "Switch to" msgstr "" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "" +#~ msgid "Transaction failed" +#~ msgstr "" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" diff --git a/apps/web/src/locales/pseudo.po b/apps/web/src/locales/pseudo.po index e6ef3f54d..04db08333 100644 --- a/apps/web/src/locales/pseudo.po +++ b/apps/web/src/locales/pseudo.po @@ -1850,7 +1850,6 @@ msgstr "" msgid "Approve" msgstr "" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "" @@ -2854,8 +2853,8 @@ msgid "Switch to" msgstr "" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "" +#~ msgid "Transaction failed" +#~ msgstr "" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" diff --git a/apps/web/src/locales/vi-VN.po b/apps/web/src/locales/vi-VN.po index 5c6e4c0af..49bc33a37 100644 --- a/apps/web/src/locales/vi-VN.po +++ b/apps/web/src/locales/vi-VN.po @@ -1906,7 +1906,6 @@ msgstr "" msgid "Approve" msgstr "Phê duyệt" -#: src/store/transactions/hooks.tsx #: src/store/transactions/hooks.tsx msgid "Processing transaction..." msgstr "Đang xử lý giao dịch..." @@ -2924,8 +2923,8 @@ msgid "Switch to" msgstr "" #: src/store/transactions/hooks.tsx -msgid "Transaction failed" -msgstr "" +#~ msgid "Transaction failed" +#~ msgstr "" #: src/app/pages/trade/xswap/_components/SwapDescription.tsx msgid "1W" From 56bc4b9ee6adbc59068f489a9680441cd7363016 Mon Sep 17 00:00:00 2001 From: hetfly Date: Thu, 17 Apr 2025 11:03:02 +0200 Subject: [PATCH 15/24] feat: add collateral transaction items --- .../components/RecentActivity/HistoryItem.tsx | 7 ++- .../app/components/RecentActivity/index.tsx | 2 +- .../transactions/BridgeTransaction.tsx | 11 ++-- .../transactions/DepositTransaction.tsx | 58 ++++++++++++++++-- .../transactions/MMSwapTransaction.tsx | 4 +- .../transactions/SwapTransaction.tsx | 61 ++++--------------- ...tatus.tsx => TransactionStatusDisplay.tsx} | 5 +- .../app/components/home/CollateralPanel.tsx | 49 ++++++++++++++- apps/web/src/store/transactions/updater.tsx | 10 ++- packages/xwagmi/src/xcall/types.ts | 4 ++ .../src/xcall/zustand/useXMessageStore.tsx | 10 ++- 11 files changed, 147 insertions(+), 74 deletions(-) rename apps/web/src/app/components/RecentActivity/transactions/{TransactionStatus.tsx => TransactionStatusDisplay.tsx} (86%) diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index d79ba0818..6ded2dd22 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -3,7 +3,7 @@ import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; import { motion } from 'framer-motion'; import React from 'react'; import BridgeTransaction from './transactions/BridgeTransaction'; -import DepositTransaction from './transactions/DepositTransaction'; +import CollateralTransaction from './transactions/DepositTransaction'; import MMSwapTransaction from './transactions/MMSwapTransaction'; import SwapTransaction from './transactions/SwapTransaction'; @@ -25,7 +25,10 @@ const HistoryItem: React.FC = ({ transaction, isMMTransaction case XTransactionType.BRIDGE: return ; case XTransactionType.DEPOSIT: - return ; + case XTransactionType.WITHDRAW: + case XTransactionType.DEPOSIT_ON_ICON: + case XTransactionType.WITHDRAW_ON_ICON: + return ; default: return
    Unknown Transaction Type - {transaction.type}
    ; } diff --git a/apps/web/src/app/components/RecentActivity/index.tsx b/apps/web/src/app/components/RecentActivity/index.tsx index ddca717aa..5da7e40f0 100644 --- a/apps/web/src/app/components/RecentActivity/index.tsx +++ b/apps/web/src/app/components/RecentActivity/index.tsx @@ -14,7 +14,7 @@ const Wrap = styled.div` overflow-y: auto; list-style: none; padding: 0 25px; - margin-bottom: 0; + margin: 0; } `; diff --git a/apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx index 3d9de79ea..311661796 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/BridgeTransaction.tsx @@ -2,13 +2,14 @@ import { useOraclePrices } from '@/store/oracle/hooks'; import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; import { formatBalance } from '@/utils/formatter'; -import { XTransaction, xChainMap } from '@balancednetwork/xwagmi'; -import React from 'react'; +import { XTransaction, XTransactionStatus, xChainMap } from '@balancednetwork/xwagmi'; +import React, { useMemo } from 'react'; import { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; import { ElapsedTime } from './_styledComponents'; -import { Amount, Meta, Status } from './_styledComponents'; -import { Container, Details, Title } from './_styledComponents'; +import { Amount, Meta, Title } from './_styledComponents'; +import { Container, Details } from './_styledComponents'; interface BridgeTransactionProps { transaction: XTransaction; @@ -39,7 +40,7 @@ const BridgeTransaction: React.FC = ({ transaction }) => - Completed + {formatRelativeTime(elapsedTime)} diff --git a/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx index e59edfdc2..49a1ec162 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx @@ -1,12 +1,58 @@ -import { XTransaction } from '@balancednetwork/xwagmi'; +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance, formatSymbol } from '@/utils/formatter'; +import { Transaction, XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; import React from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; -interface DepositTransactionProps { - transaction: XTransaction; +interface CollateralTransactionProps { + transaction: XTransaction | Transaction; } -const DepositTransaction: React.FC = ({ transaction }) => { - return
    Deposit Transaction
    ; +const CollateralTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const input = transaction.input; + const prices = useOraclePrices(); + const inputXToken = input?.inputAmount?.currency; + const inputAmount = input?.inputAmount; + const elapsedTime = useElapsedTime(transaction?.createdAt); + + const isDeposit = + transaction.type === XTransactionType.DEPOSIT || transaction.type === XTransactionType.DEPOSIT_ON_ICON; + const isICON = + transaction.type === XTransactionType.WITHDRAW_ON_ICON || transaction.type === XTransactionType.DEPOSIT_ON_ICON; + + return ( + + +
    + + {isDeposit ? 'Deposit' : 'Withdraw'} {formatSymbol(inputXToken.symbol)} collateral + + + {inputAmount && + formatBalance( + inputAmount.multiply(isDeposit ? 1 : -1).toExact(), + prices?.[inputXToken.symbol]?.toFixed() || 1, + )}{' '} + {formatSymbol(inputXToken.symbol)} + +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); }; -export default DepositTransaction; +export default CollateralTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx index 506b9b565..c3a515903 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx @@ -11,7 +11,7 @@ import React, { useState } from 'react'; import { Flex } from 'rebass'; import styled, { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; -import TransactionStatus from './TransactionStatus'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; enum CancelStatus { @@ -80,7 +80,7 @@ const MMSwapTransaction: React.FC = ({ transaction }) => - + {formatRelativeTime(elapsedTime)} diff --git a/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx index baea6cf02..50b421275 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx @@ -2,12 +2,12 @@ import { useOraclePrices } from '@/store/oracle/hooks'; import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; import { formatBalance } from '@/utils/formatter'; -import { CurrencyAmount, Token } from '@balancednetwork/sdk-core'; -import { Transaction, TransactionStatus as TxStatus, XTransaction, xMessageActions } from '@balancednetwork/xwagmi'; +import { CurrencyAmount } from '@balancednetwork/sdk-core'; +import { Transaction, XTransaction } from '@balancednetwork/xwagmi'; import React, { useMemo } from 'react'; -import styled, { useTheme } from 'styled-components'; +import { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; -import TransactionStatus from './TransactionStatus'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; interface SwapTransactionProps { @@ -18,54 +18,15 @@ const SwapTransaction: React.FC = ({ transaction }) => { const theme = useTheme(); // Add null checks for input and its properties - const inputXToken = transaction?.input?.inputAmount?.currency; - const outputXToken = transaction?.input?.outputAmount?.currency; - - // Get raw numeric values - const rawInputAmount = transaction?.input?.inputAmount?.quotient?.toString(); - const rawOutputAmount = transaction?.input?.outputAmount?.quotient?.toString(); - - // Reconstruct CurrencyAmount objects if we have the raw data - const inputAmount = useMemo(() => { - if (!rawInputAmount || !inputXToken) return null; - try { - return CurrencyAmount.fromRawAmount(inputXToken, rawInputAmount); - } catch (e) { - console.error('Failed to reconstruct input amount:', e); - return null; - } - }, [rawInputAmount, inputXToken]); - - const outputAmount = useMemo(() => { - if (!rawOutputAmount || !outputXToken) return null; - try { - return CurrencyAmount.fromRawAmount(outputXToken, rawOutputAmount); - } catch (e) { - console.error('Failed to reconstruct output amount:', e); - return null; - } - }, [rawOutputAmount, outputXToken]); + const input = transaction?.input; + const inputXToken = input?.inputAmount?.currency; + const outputXToken = input?.outputAmount?.currency; const prices = useOraclePrices(); // const primaryMessage = xMessageActions.getOf(transaction.id, true); const elapsedTime = useElapsedTime(transaction?.createdAt); - // Map transaction status to display status - const displayStatus = useMemo(() => { - if (!transaction?.status) return 'pending'; - switch (transaction.status) { - case TxStatus.pending: - return 'pending'; - case TxStatus.success: - return 'completed'; - case TxStatus.failure: - return 'failed'; - default: - return 'pending'; - } - }, [transaction?.status]); - // Guard against missing data if (!inputXToken || !outputXToken) { return null; @@ -84,14 +45,16 @@ const SwapTransaction: React.FC = ({ transaction }) => { Swap {inputXToken.symbol} for {outputXToken.symbol} - {inputAmount && formatBalance(inputAmount.toExact(), prices?.[inputXToken.symbol]?.toFixed() || 1)}{' '} + {input.inputAmount && + formatBalance(input.inputAmount.toExact(), prices?.[inputXToken.symbol]?.toFixed() || 1)}{' '} {inputXToken.symbol} for{' '} - {outputAmount && formatBalance(outputAmount.toExact(), prices[outputXToken.address]?.toFixed() || 1)}{' '} + {input.outputAmount && + formatBalance(input.outputAmount.toExact(), prices[outputXToken.symbol]?.toFixed() || 1)}{' '} {outputXToken.symbol} - + {formatRelativeTime(elapsedTime)} diff --git a/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx b/apps/web/src/app/components/RecentActivity/transactions/TransactionStatusDisplay.tsx similarity index 86% rename from apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx rename to apps/web/src/app/components/RecentActivity/transactions/TransactionStatusDisplay.tsx index 87a59af73..4aed4773d 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/TransactionStatus.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/TransactionStatusDisplay.tsx @@ -15,7 +15,7 @@ const SpinnerWrap = styled.div` top: -4px; `; -const TransactionStatus: React.FC = ({ status }) => { +const TransactionStatusDisplay: React.FC = ({ status }) => { const getStatusText = (status: string) => { switch (status.toLowerCase()) { case 'pending': @@ -28,6 +28,7 @@ const TransactionStatus: React.FC = ({ status }) => { ); case 'completed': + case 'success': return 'Completed'; case 'failed': return 'Failed'; @@ -41,4 +42,4 @@ const TransactionStatus: React.FC = ({ status }) => { return {getStatusText(status)}; }; -export default TransactionStatus; +export default TransactionStatusDisplay; diff --git a/apps/web/src/app/components/home/CollateralPanel.tsx b/apps/web/src/app/components/home/CollateralPanel.tsx index 3e33e5294..271094aaa 100644 --- a/apps/web/src/app/components/home/CollateralPanel.tsx +++ b/apps/web/src/app/components/home/CollateralPanel.tsx @@ -23,6 +23,7 @@ import IconUnstakeSICX from '@/assets/icons/timer-color.svg'; import IconKeepSICX from '@/assets/icons/wallet-tick-color.svg'; import { NETWORK_ID } from '@/constants/config'; import { SLIDER_RANGE_MAX_BOTTOM_THRESHOLD } from '@/constants/index'; +import { SUPPORTED_TOKENS_LIST, wICX } from '@/constants/tokens'; import { MODAL_ID, modalActions } from '@/hooks/useModalStore'; import useWidth from '@/hooks/useWidth'; import { useICXUnstakingTime } from '@/store/application/hooks'; @@ -41,7 +42,8 @@ import { useHasEnoughICX } from '@/store/wallet/hooks'; import { parseUnits } from '@/utils'; import { formatSymbol, useWrongSymbol } from '@/utils/formatter'; import { showMessageOnBeforeUnload } from '@/utils/messages'; -import { XTransactionType, getXChainType } from '@balancednetwork/xwagmi'; +import { CurrencyAmount } from '@balancednetwork/sdk-core'; +import { XTransactionType, getICONXTransactionInput, getXChainType } from '@balancednetwork/xwagmi'; import { xChainMap } from '@balancednetwork/xwagmi'; import { useXConnect, useXConnectors } from '@balancednetwork/xwagmi'; import { bnJs } from '@balancednetwork/xwagmi'; @@ -214,6 +216,10 @@ const CollateralPanel = () => { const cx = bnJs.inject({ account }).getContract(collateralTokenAddress!); const decimals: string = await cx.decimals(); + if (!account) { + return; + } + if (shouldDeposit) { try { if (isHandlingICX) { @@ -226,8 +232,12 @@ const CollateralPanel = () => { { pending: t`Depositing collateral...`, summary: t`Deposited ${collateralDifference.dp(2).toFormat()} ICX as collateral.`, - //todo - input: undefined, + type: XTransactionType.DEPOSIT_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.DEPOSIT_ON_ICON, + CurrencyAmount.fromRawAmount(wICX[1], collateralDifference.times(10 ** 18).toFixed(0)), + ), }, ); } else { @@ -245,6 +255,15 @@ const CollateralPanel = () => { summary: t`Deposited ${collateralDifference.toFixed( collateralDecimalPlaces, )} ${collateralType} as collateral.`, + type: XTransactionType.DEPOSIT_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.DEPOSIT_ON_ICON, + CurrencyAmount.fromRawAmount( + SUPPORTED_TOKENS_LIST.find(token => token.symbol === collateralType)!, + collateralDifference.times(10 ** Number(decimals)).toFixed(0), + ), + ), }, ); } @@ -275,6 +294,12 @@ const CollateralPanel = () => { { pending: t`Withdrawing collateral...`, summary: t`${collateralDifference.dp(2).toFormat()} ICX is unstaking.`, + type: XTransactionType.WITHDRAW_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.WITHDRAW_ON_ICON, + CurrencyAmount.fromRawAmount(wICX[1], collateralDifference.times(10 ** 18).toFixed(0)), + ), }, ); } else { @@ -292,6 +317,15 @@ const CollateralPanel = () => { summary: t`${collateralDifferenceInSICX .dp(collateralDecimalPlaces) .toFormat()} ${formatSymbol(collateralType)} added to your wallet.`, + type: XTransactionType.WITHDRAW_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.WITHDRAW_ON_ICON, + CurrencyAmount.fromRawAmount( + SUPPORTED_TOKENS_LIST.find(token => token.symbol === 'sICX')!, + collateralDifference.times(10 ** 18).toFixed(0), + ), + ), }, ); } @@ -310,6 +344,15 @@ const CollateralPanel = () => { summary: t`${collateralDifference .dp(collateralDecimalPlaces) .toFormat()} ${formatSymbol(collateralType)} added to your wallet.`, + type: XTransactionType.WITHDRAW_ON_ICON, + input: getICONXTransactionInput( + account, + XTransactionType.WITHDRAW_ON_ICON, + CurrencyAmount.fromRawAmount( + SUPPORTED_TOKENS_LIST.find(token => token.symbol === collateralType)!, + collateralDifference.times(10 ** Number(decimals)).toFixed(0), + ), + ), }, ); } diff --git a/apps/web/src/store/transactions/updater.tsx b/apps/web/src/store/transactions/updater.tsx index 3ce5e73eb..94ca71323 100644 --- a/apps/web/src/store/transactions/updater.tsx +++ b/apps/web/src/store/transactions/updater.tsx @@ -31,9 +31,15 @@ export default function Updater(): null { const transactions = useAllTransactions(); const xTransactions = useXTransactionStore(state => - Object.values(state.transactions).filter(tx => tx.type === XTransactionType.SWAP_ON_ICON), + Object.values(state.transactions).filter( + tx => + tx.type === XTransactionType.SWAP_ON_ICON || + tx.type === XTransactionType.DEPOSIT_ON_ICON || + tx.type === XTransactionType.WITHDRAW_ON_ICON || + tx.type === XTransactionType.BORROW_ON_ICON || + tx.type === XTransactionType.REPAY_ON_ICON, + ), ); - // biome-ignore lint/correctness/useExhaustiveDependencies: React.useEffect(() => { if (!networkId || !iconService) return; diff --git a/packages/xwagmi/src/xcall/types.ts b/packages/xwagmi/src/xcall/types.ts index 756e5466e..44b43f705 100644 --- a/packages/xwagmi/src/xcall/types.ts +++ b/packages/xwagmi/src/xcall/types.ts @@ -37,9 +37,13 @@ export enum XTransactionType { // lend & borrow DEPOSIT = 'deposit', + DEPOSIT_ON_ICON = 'deposit_on_icon', WITHDRAW = 'withdraw', + WITHDRAW_ON_ICON = 'withdraw_on_icon', BORROW = 'borrow', + BORROW_ON_ICON = 'borrow_on_icon', REPAY = 'repay', + REPAY_ON_ICON = 'repay_on_icon', //liquidity LP_DEPOSIT_XTOKEN = 'lp_deposit_xtoken', diff --git a/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx b/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx index 5887225d5..7e1a04866 100644 --- a/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx +++ b/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx @@ -177,8 +177,14 @@ export const useXMessageStore = create()( const xTransaction = xTransactionActions.get(xMessage.xTransactionId); if (!xTransaction) return; - // Skip XMessage handling for SWAP_ON_ICON transactions - if (xTransaction.type === XTransactionType.SWAP_ON_ICON) { + // Skip XMessage handling for ICON transactions + if ( + xTransaction.type === XTransactionType.SWAP_ON_ICON || + xTransaction.type === XTransactionType.DEPOSIT_ON_ICON || + xTransaction.type === XTransactionType.WITHDRAW_ON_ICON || + xTransaction.type === XTransactionType.BORROW_ON_ICON || + xTransaction.type === XTransactionType.REPAY_ON_ICON + ) { return; } From 041fbb22720c5141bae0d43e910ba4187588859b Mon Sep 17 00:00:00 2001 From: hetfly Date: Thu, 17 Apr 2025 11:07:45 +0200 Subject: [PATCH 16/24] chore: cleanup --- .../src/app/components/RecentActivity/HistoryItem.tsx | 2 +- ...positTransaction.tsx => CollateralTransaction.tsx} | 4 ++-- .../RecentActivity/transactions/MMSwapTransaction.tsx | 1 - .../RecentActivity/transactions/SwapTransaction.tsx | 11 +++-------- 4 files changed, 6 insertions(+), 12 deletions(-) rename apps/web/src/app/components/RecentActivity/transactions/{DepositTransaction.tsx => CollateralTransaction.tsx} (94%) diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index 6ded2dd22..11ec3e45a 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -3,7 +3,7 @@ import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; import { motion } from 'framer-motion'; import React from 'react'; import BridgeTransaction from './transactions/BridgeTransaction'; -import CollateralTransaction from './transactions/DepositTransaction'; +import CollateralTransaction from './transactions/CollateralTransaction'; import MMSwapTransaction from './transactions/MMSwapTransaction'; import SwapTransaction from './transactions/SwapTransaction'; diff --git a/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/CollateralTransaction.tsx similarity index 94% rename from apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx rename to apps/web/src/app/components/RecentActivity/transactions/CollateralTransaction.tsx index 49a1ec162..f54281e47 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/DepositTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/CollateralTransaction.tsx @@ -2,7 +2,7 @@ import { useOraclePrices } from '@/store/oracle/hooks'; import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; import { formatBalance, formatSymbol } from '@/utils/formatter'; -import { Transaction, XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; import React from 'react'; import { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; @@ -10,7 +10,7 @@ import TransactionStatusDisplay from './TransactionStatusDisplay'; import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; interface CollateralTransactionProps { - transaction: XTransaction | Transaction; + transaction: XTransaction; } const CollateralTransaction: React.FC = ({ transaction }) => { diff --git a/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx index c3a515903..83dd40a6d 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/MMSwapTransaction.tsx @@ -5,7 +5,6 @@ import { MMTransaction, MMTransactionActions, MMTransactionStatus } from '@/stor import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; import { formatBalance, formatSymbol } from '@/utils/formatter'; -import { XTransaction, xMessageActions } from '@balancednetwork/xwagmi'; import { xChainMap } from '@balancednetwork/xwagmi'; import React, { useState } from 'react'; import { Flex } from 'rebass'; diff --git a/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx index 50b421275..fa9b47621 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/SwapTransaction.tsx @@ -2,29 +2,24 @@ import { useOraclePrices } from '@/store/oracle/hooks'; import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; import { formatBalance } from '@/utils/formatter'; -import { CurrencyAmount } from '@balancednetwork/sdk-core'; -import { Transaction, XTransaction } from '@balancednetwork/xwagmi'; -import React, { useMemo } from 'react'; +import { XTransaction } from '@balancednetwork/xwagmi'; +import React from 'react'; import { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; import TransactionStatusDisplay from './TransactionStatusDisplay'; import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; interface SwapTransactionProps { - transaction: XTransaction | Transaction; + transaction: XTransaction; } const SwapTransaction: React.FC = ({ transaction }) => { const theme = useTheme(); - - // Add null checks for input and its properties const input = transaction?.input; const inputXToken = input?.inputAmount?.currency; const outputXToken = input?.outputAmount?.currency; - const prices = useOraclePrices(); // const primaryMessage = xMessageActions.getOf(transaction.id, true); - const elapsedTime = useElapsedTime(transaction?.createdAt); // Guard against missing data From 72f7f275915aea07ddd4c5848f48f30c6c1a37af Mon Sep 17 00:00:00 2001 From: hetfly Date: Thu, 17 Apr 2025 11:36:58 +0200 Subject: [PATCH 17/24] fix: ensure positive token amount --- .../RecentActivity/transactions/CollateralTransaction.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/components/RecentActivity/transactions/CollateralTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/CollateralTransaction.tsx index f54281e47..1c8e306af 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/CollateralTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/CollateralTransaction.tsx @@ -36,12 +36,13 @@ const CollateralTransaction: React.FC = ({ transacti />
    - {isDeposit ? 'Deposit' : 'Withdraw'} {formatSymbol(inputXToken.symbol)} collateral + {isDeposit ? 'Deposit' : inputXToken.symbol === 'wICX' ? 'Unstake' : 'Withdraw'}{' '} + {formatSymbol(inputXToken.symbol)} collateral {inputAmount && formatBalance( - inputAmount.multiply(isDeposit ? 1 : -1).toExact(), + inputAmount.multiply(inputAmount.lessThan(0) ? -1 : 1).toExact(), prices?.[inputXToken.symbol]?.toFixed() || 1, )}{' '} {formatSymbol(inputXToken.symbol)} From 7408932731351fce67f5a6f608746b9d6747678f Mon Sep 17 00:00:00 2001 From: hetfly Date: Fri, 18 Apr 2025 09:38:02 +0200 Subject: [PATCH 18/24] feat: add loan transaction history items --- .../components/RecentActivity/HistoryItem.tsx | 6 +++ .../transactions/LoanTransaction.tsx | 51 +++++++++++++++++++ .../web/src/app/components/home/LoanPanel.tsx | 16 +++++- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/app/components/RecentActivity/transactions/LoanTransaction.tsx diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index 11ec3e45a..2aa27845f 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -4,6 +4,7 @@ import { motion } from 'framer-motion'; import React from 'react'; import BridgeTransaction from './transactions/BridgeTransaction'; import CollateralTransaction from './transactions/CollateralTransaction'; +import LoanTransaction from './transactions/LoanTransaction'; import MMSwapTransaction from './transactions/MMSwapTransaction'; import SwapTransaction from './transactions/SwapTransaction'; @@ -29,6 +30,11 @@ const HistoryItem: React.FC = ({ transaction, isMMTransaction case XTransactionType.DEPOSIT_ON_ICON: case XTransactionType.WITHDRAW_ON_ICON: return ; + case XTransactionType.BORROW: + case XTransactionType.REPAY: + case XTransactionType.BORROW_ON_ICON: + case XTransactionType.REPAY_ON_ICON: + return ; default: return
    Unknown Transaction Type - {transaction.type}
    ; } diff --git a/apps/web/src/app/components/RecentActivity/transactions/LoanTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/LoanTransaction.tsx new file mode 100644 index 000000000..1a09d07c0 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/LoanTransaction.tsx @@ -0,0 +1,51 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance, formatSymbol } from '@/utils/formatter'; +import { XChainId, XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import React from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; + +interface LoanTransactionProps { + transaction: XTransaction; +} + +const LoanTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const input = transaction.input; + const inputXToken = input?.inputAmount?.currency; + const inputAmount = input?.inputAmount; + const elapsedTime = useElapsedTime(transaction?.createdAt); + + const isBorrow = transaction.type === XTransactionType.BORROW || transaction.type === XTransactionType.BORROW_ON_ICON; + + return ( + + +
    + + {isBorrow ? 'Borrow' : 'Repay'} + {' bnUSD'} + + + {inputAmount && formatBalance(inputAmount.multiply(inputAmount.lessThan(0) ? -1 : 1).toExact(), 1)} + {' bnUSD'} + +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default LoanTransaction; diff --git a/apps/web/src/app/components/home/LoanPanel.tsx b/apps/web/src/app/components/home/LoanPanel.tsx index 740821d91..f3b545dc0 100644 --- a/apps/web/src/app/components/home/LoanPanel.tsx +++ b/apps/web/src/app/components/home/LoanPanel.tsx @@ -30,13 +30,15 @@ import { useTransactionAdder } from '@/store/transactions/hooks'; import { useHasEnoughICX } from '@/store/wallet/hooks'; import { parseUnits } from '@/utils'; import { showMessageOnBeforeUnload } from '@/utils/messages'; -import { bnJs } from '@balancednetwork/xwagmi'; +import { XTransactionType, bnJs, getICONXTransactionInput } from '@balancednetwork/xwagmi'; import ModalContent from '@/app/components/ModalContent'; +import { bnUSD } from '@/constants/tokens'; import { MODAL_ID, modalActions } from '@/hooks/useModalStore'; import useWidth from '@/hooks/useWidth'; import { useIconReact } from '@/packages/icon-react'; import { useWrongSymbol } from '@/utils/formatter'; +import { CurrencyAmount } from '@balancednetwork/sdk-core'; import { ICON_XCALL_NETWORK_ID } from '@balancednetwork/xwagmi'; import BigNumber from 'bignumber.js'; import { PanelInfoItem, PanelInfoWrap, UnderPanel } from './CollateralPanel'; @@ -152,6 +154,12 @@ const LoanPanel = () => { { pending: t`Borrowing bnUSD...`, summary: t`Borrowed ${differenceAmount.dp(2).toFormat()} bnUSD.`, + type: XTransactionType.BORROW_ON_ICON, + input: getICONXTransactionInput( + iconAccount, + XTransactionType.BORROW_ON_ICON, + CurrencyAmount.fromRawAmount(bnUSD[1], differenceAmount.times(10 ** 18).toFixed(0)), + ), }, ); // close modal @@ -184,6 +192,12 @@ const LoanPanel = () => { { pending: t`Repaying bnUSD...`, summary: t`Repaid ${repayAmount.dp(2).toFormat()} bnUSD.`, + type: XTransactionType.REPAY_ON_ICON, + input: getICONXTransactionInput( + iconAccount, + XTransactionType.REPAY_ON_ICON, + CurrencyAmount.fromRawAmount(bnUSD[1], repayAmount.times(10 ** 18).toFixed(0)), + ), }, ); // close modal From 2c12c621708799d9a3c43050f85dfe70c25733ff Mon Sep 17 00:00:00 2001 From: hetfly Date: Sat, 19 Apr 2025 12:35:36 +0200 Subject: [PATCH 19/24] feat: add LP transaction items --- .../components/RecentActivity/HistoryItem.tsx | 24 +++++- .../transactions/DepositXTokenTransaction.tsx | 51 +++++++++++++ .../transactions/LPStakeTransaction.tsx | 48 ++++++++++++ .../transactions/LPTransaction.tsx | 73 +++++++++++++++++++ 4 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/app/components/RecentActivity/transactions/DepositXTokenTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index 2aa27845f..de867c75b 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -4,6 +4,9 @@ import { motion } from 'framer-motion'; import React from 'react'; import BridgeTransaction from './transactions/BridgeTransaction'; import CollateralTransaction from './transactions/CollateralTransaction'; +import DepositXTokenTransaction from './transactions/DepositXTokenTransaction'; +import LPStakeTransaction from './transactions/LPStakeTransaction'; +import LPTransaction from './transactions/LPTransaction'; import LoanTransaction from './transactions/LoanTransaction'; import MMSwapTransaction from './transactions/MMSwapTransaction'; import SwapTransaction from './transactions/SwapTransaction'; @@ -20,21 +23,40 @@ const HistoryItem: React.FC = ({ transaction, isMMTransaction } switch (transaction.type) { + //Swap case XTransactionType.SWAP: case XTransactionType.SWAP_ON_ICON: return ; + + //Bridge case XTransactionType.BRIDGE: return ; + + // Collateral + case XTransactionType.DEPOSIT_ON_ICON: case XTransactionType.DEPOSIT: case XTransactionType.WITHDRAW: - case XTransactionType.DEPOSIT_ON_ICON: case XTransactionType.WITHDRAW_ON_ICON: return ; + + //Loan case XTransactionType.BORROW: case XTransactionType.REPAY: case XTransactionType.BORROW_ON_ICON: case XTransactionType.REPAY_ON_ICON: return ; + + //Liquidity + case XTransactionType.LP_REMOVE_LIQUIDITY: + case XTransactionType.LP_ADD_LIQUIDITY: + return ; + case XTransactionType.LP_DEPOSIT_XTOKEN: + case XTransactionType.LP_WITHDRAW_XTOKEN: + return ; + case XTransactionType.LP_UNSTAKE: + case XTransactionType.LP_STAKE: + return ; + default: return
    Unknown Transaction Type - {transaction.type}
    ; } diff --git a/apps/web/src/app/components/RecentActivity/transactions/DepositXTokenTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/DepositXTokenTransaction.tsx new file mode 100644 index 000000000..104034902 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/DepositXTokenTransaction.tsx @@ -0,0 +1,51 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance, formatSymbol } from '@/utils/formatter'; +import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import React from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; + +interface DepositXTokenTransactionProps { + transaction: XTransaction; +} + +const DepositXTokenTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const input = transaction?.input; + const inputXToken = input?.inputAmount?.currency; + const prices = useOraclePrices(); + const elapsedTime = useElapsedTime(transaction?.createdAt); + + const isDeposit = transaction.type === XTransactionType.LP_DEPOSIT_XTOKEN; + + return ( + + +
    + + {isDeposit ? 'Supply' : 'Withdraw'} {formatSymbol(inputXToken?.symbol)}{' '} + + + {input?.inputAmount && + formatBalance(input.inputAmount.toExact(), prices?.[inputXToken?.symbol]?.toFixed() || 1)}{' '} + {inputXToken?.symbol} + +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default DepositXTokenTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx new file mode 100644 index 000000000..97db8797c --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx @@ -0,0 +1,48 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance, formatSymbol } from '@/utils/formatter'; +import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import React from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; + +interface LPStakeTransactionProps { + transaction: XTransaction; +} + +const LPStakeTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const input = transaction?.input; + const inputXToken = input?.inputAmount?.currency; + const symbolA = input?.tokenASymbol; + const symbolB = input?.tokenBSymbol; + const elapsedTime = useElapsedTime(transaction?.createdAt); + + const isStake = transaction.type === XTransactionType.LP_STAKE; + + return ( + + +
    + + {isStake ? 'Stake' : 'Unstake'} {formatSymbol(symbolA)} / {formatSymbol(symbolB)} + + {input.inputAmount && input.inputAmount.toSignificant(5)} LP +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default LPStakeTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx new file mode 100644 index 000000000..5886627e9 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx @@ -0,0 +1,73 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance, formatSymbol } from '@/utils/formatter'; +import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import React from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; + +interface LPTransactionProps { + transaction: XTransaction; +} + +const LPTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const input = transaction?.input; + const inputXToken = input?.inputAmount?.currency; + const prices = useOraclePrices(); + // const primaryMessage = xMessageActions.getOf(transaction.id, true); + const elapsedTime = useElapsedTime(transaction?.createdAt); + + const isWithdraw = transaction.type === XTransactionType.LP_REMOVE_LIQUIDITY; + + const withdrawAmountA = input?.withdrawAmountA; + const withdrawAmountB = input?.withdrawAmountB; + + const addAmountA = input?.inputAmount; + const addAmountB = input?.outputAmount; + + return ( + + +
    + {isWithdraw ? ( + {`Withdraw ${formatSymbol(withdrawAmountA?.currency.symbol)} / ${formatSymbol(withdrawAmountB?.currency.symbol)}`} + ) : ( + {`Supply ${formatSymbol(addAmountA?.currency.symbol)} / ${formatSymbol(addAmountB?.currency.symbol)}`} + )} + + {isWithdraw ? ( + + {formatBalance(withdrawAmountA?.toExact(), prices?.[withdrawAmountA?.currency.symbol]?.toFixed() || 1)}{' '} + {withdrawAmountA?.currency.symbol} + {' / '} + {formatBalance(withdrawAmountB?.toExact(), prices?.[withdrawAmountB?.currency.symbol]?.toFixed() || 1)}{' '} + {withdrawAmountB?.currency.symbol} + + ) : ( + + {formatBalance(addAmountA?.toExact(), prices?.[addAmountA?.currency.symbol]?.toFixed() || 1)}{' '} + {addAmountA?.currency.symbol} + {' / '} + {formatBalance(addAmountB?.toExact(), prices?.[addAmountB?.currency.symbol]?.toFixed() || 1)}{' '} + {addAmountB?.currency.symbol} + + )} +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default LPTransaction; From 3eede8d7f511d2b183d3bff56d3944200ec36ff8 Mon Sep 17 00:00:00 2001 From: hetfly Date: Sat, 19 Apr 2025 19:04:16 +0200 Subject: [PATCH 20/24] feat: update LP items logos --- .../components/PoolLogoWithNetwork/index.tsx | 47 +++++++++++++--- .../transactions/LPStakeTransaction.tsx | 21 ++++--- .../transactions/LPTransaction.tsx | 56 +++++++------------ 3 files changed, 72 insertions(+), 52 deletions(-) diff --git a/apps/web/src/app/components/PoolLogoWithNetwork/index.tsx b/apps/web/src/app/components/PoolLogoWithNetwork/index.tsx index e40a3551b..e0ddf2960 100644 --- a/apps/web/src/app/components/PoolLogoWithNetwork/index.tsx +++ b/apps/web/src/app/components/PoolLogoWithNetwork/index.tsx @@ -8,7 +8,7 @@ import { xChainMap } from '@balancednetwork/xwagmi'; import { ChainLogo } from '../ChainLogo'; import CurrencyLogo from '../CurrencyLogo'; -const IconWrapper = styled(Box)<{ $respoVersion?: boolean }>` +const IconWrapper = styled(Box)<{ $respoVersion?: boolean; $compactVersion?: boolean }>` width: 48px; height: 48px; border-radius: 50%; @@ -39,9 +39,17 @@ const IconWrapper = styled(Box)<{ $respoVersion?: boolean }>` } `} `}; + + ${({ $compactVersion }) => + $compactVersion && + css` + width: 25px; + height: 25px; + background-color: rgb(12, 42, 77); + `} `; -const NetworkWrap = styled.div` +const NetworkWrap = styled.div<{ $compactVersion?: boolean }>` position: absolute; border-radius: 50%; width: 14px; @@ -58,12 +66,33 @@ const NetworkWrap = styled.div` top: 0; left: 0; } + + ${({ $compactVersion }) => + $compactVersion && + css` + width: 10px; + height: 10px; + outline: 1px solid #0c2a4d; + bottom: -3px; + margin-left: -6px; + + img { + width: 10px; + height: 10px; + } + `} `; -export const PoolLogoWrapper = styled(Box)` -position: relative; +export const PoolLogoWrapper = styled(Box)<{ $compactVersion?: boolean }>` + position: relative; display: flex; min-width: 80px; + + ${({ $compactVersion }) => + $compactVersion && + css` + min-width: 40px; + `} `; function PoolLogoWithNetwork({ @@ -71,21 +100,23 @@ function PoolLogoWithNetwork({ quoteCurrency, respoVersion, chainId, + compactVersion, }: { baseCurrency: Currency; quoteCurrency: Currency; respoVersion?: boolean; chainId: XChainId; + compactVersion?: boolean; }) { return ( - - + + - + - + diff --git a/apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx index 97db8797c..e212a0a99 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/LPStakeTransaction.tsx @@ -1,3 +1,4 @@ +import { SUPPORTED_TOKENS_LIST } from '@/constants/tokens'; import { useOraclePrices } from '@/store/oracle/hooks'; import { useElapsedTime } from '@/store/user/hooks'; import { formatRelativeTime } from '@/utils'; @@ -6,6 +7,7 @@ import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; import React from 'react'; import { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import PoolLogoWithNetwork from '../../PoolLogoWithNetwork'; import TransactionStatusDisplay from './TransactionStatusDisplay'; import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; @@ -14,23 +16,26 @@ interface LPStakeTransactionProps { } const LPStakeTransaction: React.FC = ({ transaction }) => { - const theme = useTheme(); const input = transaction?.input; - const inputXToken = input?.inputAmount?.currency; const symbolA = input?.tokenASymbol; const symbolB = input?.tokenBSymbol; const elapsedTime = useElapsedTime(transaction?.createdAt); const isStake = transaction.type === XTransactionType.LP_STAKE; + const tokenA = SUPPORTED_TOKENS_LIST.find(token => token.symbol === symbolA); + const tokenB = SUPPORTED_TOKENS_LIST.find(token => token.symbol === symbolB); + return ( - + {tokenA && tokenB ? ( + + ) : null}
    {isStake ? 'Stake' : 'Unstake'} {formatSymbol(symbolA)} / {formatSymbol(symbolB)} diff --git a/apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx index 5886627e9..d5b39f5dd 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx +++ b/apps/web/src/app/components/RecentActivity/transactions/LPTransaction.tsx @@ -6,6 +6,7 @@ import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; import React from 'react'; import { useTheme } from 'styled-components'; import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import PoolLogoWithNetwork from '../../PoolLogoWithNetwork'; import TransactionStatusDisplay from './TransactionStatusDisplay'; import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; @@ -14,53 +15,36 @@ interface LPTransactionProps { } const LPTransaction: React.FC<LPTransactionProps> = ({ transaction }) => { - const theme = useTheme(); const input = transaction?.input; - const inputXToken = input?.inputAmount?.currency; const prices = useOraclePrices(); // const primaryMessage = xMessageActions.getOf(transaction.id, true); const elapsedTime = useElapsedTime(transaction?.createdAt); const isWithdraw = transaction.type === XTransactionType.LP_REMOVE_LIQUIDITY; - const withdrawAmountA = input?.withdrawAmountA; - const withdrawAmountB = input?.withdrawAmountB; - - const addAmountA = input?.inputAmount; - const addAmountB = input?.outputAmount; + const tokenAmountA = isWithdraw ? input?.withdrawAmountA : input?.inputAmount; + const tokenAmountB = isWithdraw ? input?.withdrawAmountB : input?.outputAmount; return ( <Container> - <CurrencyLogoWithNetwork - currency={inputXToken} - chainId={inputXToken.xChainId} - bgColor={theme.colors.bg2} - size="26px" - /> + {tokenAmountA && tokenAmountB ? ( + <PoolLogoWithNetwork + compactVersion={true} + chainId={transaction.sourceChainId} + baseCurrency={tokenAmountA.currency} + quoteCurrency={tokenAmountB.currency} + /> + ) : null} <Details> - {isWithdraw ? ( - <Title>{`Withdraw ${formatSymbol(withdrawAmountA?.currency.symbol)} / ${formatSymbol(withdrawAmountB?.currency.symbol)}`} - ) : ( - {`Supply ${formatSymbol(addAmountA?.currency.symbol)} / ${formatSymbol(addAmountB?.currency.symbol)}`} - )} - - {isWithdraw ? ( - - {formatBalance(withdrawAmountA?.toExact(), prices?.[withdrawAmountA?.currency.symbol]?.toFixed() || 1)}{' '} - {withdrawAmountA?.currency.symbol} - {' / '} - {formatBalance(withdrawAmountB?.toExact(), prices?.[withdrawAmountB?.currency.symbol]?.toFixed() || 1)}{' '} - {withdrawAmountB?.currency.symbol} - - ) : ( - - {formatBalance(addAmountA?.toExact(), prices?.[addAmountA?.currency.symbol]?.toFixed() || 1)}{' '} - {addAmountA?.currency.symbol} - {' / '} - {formatBalance(addAmountB?.toExact(), prices?.[addAmountB?.currency.symbol]?.toFixed() || 1)}{' '} - {addAmountB?.currency.symbol} - - )} + {`${isWithdraw ? 'Withdraw' : 'Supply'} ${formatSymbol(tokenAmountA?.currency.symbol)} / ${formatSymbol(tokenAmountB?.currency.symbol)}`} + + + {formatBalance(tokenAmountA?.toExact(), prices?.[tokenAmountA?.currency.symbol]?.toFixed() || 1)}{' '} + {formatSymbol(tokenAmountA?.currency.symbol)} + {' / '} + {formatBalance(tokenAmountB?.toExact(), prices?.[tokenAmountB?.currency.symbol]?.toFixed() || 1)}{' '} + {formatSymbol(tokenAmountB?.currency.symbol)} +
    From e1186ca70f1ac7b29b940464cd55c2898c31de7c Mon Sep 17 00:00:00 2001 From: hetfly Date: Sun, 20 Apr 2025 19:38:19 +0200 Subject: [PATCH 21/24] feat: add rewards and savings history items --- .../components/RecentActivity/HistoryItem.tsx | 17 +++++++ .../transactions/RewardsFeesTransaction.tsx | 44 ++++++++++++++++ .../transactions/RewardsLPTransaction.tsx | 51 +++++++++++++++++++ .../RewardsSavingsTransaction.tsx | 51 +++++++++++++++++++ .../transactions/SavingsTransaction.tsx | 48 +++++++++++++++++ .../home/RewardsPanel/NetworkFeesRewards.tsx | 11 +++- apps/web/src/store/transactions/updater.tsx | 3 +- packages/xwagmi/src/xcall/types.ts | 3 ++ .../src/xcall/zustand/useXMessageStore.tsx | 3 +- 9 files changed, 228 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/app/components/RecentActivity/transactions/RewardsFeesTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/RewardsLPTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/RewardsSavingsTransaction.tsx create mode 100644 apps/web/src/app/components/RecentActivity/transactions/SavingsTransaction.tsx diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index de867c75b..60b210db2 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -9,6 +9,10 @@ import LPStakeTransaction from './transactions/LPStakeTransaction'; import LPTransaction from './transactions/LPTransaction'; import LoanTransaction from './transactions/LoanTransaction'; import MMSwapTransaction from './transactions/MMSwapTransaction'; +import RewardsFeesTransaction from './transactions/RewardsFeesTransaction'; +import RewardsLPTransaction from './transactions/RewardsLPTransaction'; +import RewardsSavingsTransaction from './transactions/RewardsSavingsTransaction'; +import SavingsTransaction from './transactions/SavingsTransaction'; import SwapTransaction from './transactions/SwapTransaction'; interface HistoryItemProps { @@ -57,6 +61,19 @@ const HistoryItem: React.FC = ({ transaction, isMMTransaction case XTransactionType.LP_STAKE: return ; + //Savings + case XTransactionType.SAVINGS_LOCK_BNUSD: + case XTransactionType.SAVINGS_UNLOCK_BNUSD: + return ; + + //Rewards + case XTransactionType.LP_CLAIM_REWARDS: + return ; + case XTransactionType.SAVINGS_CLAIM_REWARDS: + return ; + case XTransactionType.CLAIM_NETWORK_FEES: + return ; + default: return
    Unknown Transaction Type - {transaction.type}
    ; } diff --git a/apps/web/src/app/components/RecentActivity/transactions/RewardsFeesTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/RewardsFeesTransaction.tsx new file mode 100644 index 000000000..54575e998 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/RewardsFeesTransaction.tsx @@ -0,0 +1,44 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance } from '@/utils/formatter'; +import { XTransaction, XTransactionStatus, xChainMap } from '@balancednetwork/xwagmi'; +import React, { useMemo } from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { ElapsedTime } from './_styledComponents'; +import { Amount, Meta, Title } from './_styledComponents'; +import { Container, Details } from './_styledComponents'; + +interface RewardsFeesTransactionProps { + transaction: XTransaction; +} + +const RewardsFeesTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const inputXToken = transaction.input.inputAmount.currency; + const inputAmount = transaction.input.inputAmount.toFixed(); + const prices = useOraclePrices(); + + const elapsedTime = useElapsedTime(transaction.createdAt); + + return ( + + +
    + Claim network fees + {/* + {formatBalance(inputAmount, prices?.[inputXToken.symbol]?.toFixed() || 1)} {inputXToken.symbol} to{' '} + {xChainMap[destination]?.name} + */} +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default RewardsFeesTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/RewardsLPTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/RewardsLPTransaction.tsx new file mode 100644 index 000000000..ab3fc8666 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/RewardsLPTransaction.tsx @@ -0,0 +1,51 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance } from '@/utils/formatter'; +import { XTransaction, XTransactionStatus, xChainMap } from '@balancednetwork/xwagmi'; +import React, { useMemo } from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { ElapsedTime } from './_styledComponents'; +import { Amount, Meta, Title } from './_styledComponents'; +import { Container, Details } from './_styledComponents'; + +interface RewardsLPTransactionProps { + transaction: XTransaction; +} + +const RewardsLPTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const inputXToken = transaction.input.inputAmount.currency; + const inputAmount = transaction.input.inputAmount.toFixed(); + const prices = useOraclePrices(); + + console.log('rewards tx', transaction); + + const elapsedTime = useElapsedTime(transaction.createdAt); + + return ( + + +
    + Claim liquidity rewards + {/* + {formatBalance(inputAmount, prices?.[inputXToken.symbol]?.toFixed() || 1)} {inputXToken.symbol} to{' '} + {xChainMap[destination]?.name} + */} +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default RewardsLPTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/RewardsSavingsTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/RewardsSavingsTransaction.tsx new file mode 100644 index 000000000..fff196285 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/RewardsSavingsTransaction.tsx @@ -0,0 +1,51 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance } from '@/utils/formatter'; +import { XTransaction, XTransactionStatus, xChainMap } from '@balancednetwork/xwagmi'; +import React, { useMemo } from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { ElapsedTime } from './_styledComponents'; +import { Amount, Meta, Title } from './_styledComponents'; +import { Container, Details } from './_styledComponents'; + +interface RewardsSavingsTransactionProps { + transaction: XTransaction; +} + +const RewardsSavingsTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const inputXToken = transaction.input.inputAmount.currency; + const inputAmount = transaction.input.inputAmount.toFixed(); + const prices = useOraclePrices(); + + console.log('rewards tx', transaction); + + const elapsedTime = useElapsedTime(transaction.createdAt); + + return ( + + +
    + Claim savings rewards + {/* + {formatBalance(inputAmount, prices?.[inputXToken.symbol]?.toFixed() || 1)} {inputXToken.symbol} to{' '} + {xChainMap[destination]?.name} + */} +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default RewardsSavingsTransaction; diff --git a/apps/web/src/app/components/RecentActivity/transactions/SavingsTransaction.tsx b/apps/web/src/app/components/RecentActivity/transactions/SavingsTransaction.tsx new file mode 100644 index 000000000..6f0dd4f13 --- /dev/null +++ b/apps/web/src/app/components/RecentActivity/transactions/SavingsTransaction.tsx @@ -0,0 +1,48 @@ +import { useOraclePrices } from '@/store/oracle/hooks'; +import { useElapsedTime } from '@/store/user/hooks'; +import { formatRelativeTime } from '@/utils'; +import { formatBalance, formatSymbol } from '@/utils/formatter'; +import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import React from 'react'; +import { useTheme } from 'styled-components'; +import CurrencyLogoWithNetwork from '../../CurrencyLogoWithNetwork'; +import TransactionStatusDisplay from './TransactionStatusDisplay'; +import { Amount, Container, Details, ElapsedTime, Meta, Title } from './_styledComponents'; + +interface SavingsTransactionProps { + transaction: XTransaction; +} + +const SavingsTransaction: React.FC = ({ transaction }) => { + const theme = useTheme(); + const input = transaction?.input; + const inputXToken = input?.inputAmount?.currency; + const elapsedTime = useElapsedTime(transaction?.createdAt); + + const isLock = transaction.type === XTransactionType.SAVINGS_LOCK_BNUSD; + + return ( + + +
    + + {isLock ? 'Deposit' : 'Withdraw'} {formatSymbol(inputXToken?.symbol)} + + + {input?.inputAmount && formatBalance(input.inputAmount.toExact(), 1)} {inputXToken?.symbol} + +
    + + + {formatRelativeTime(elapsedTime)} + +
    + ); +}; + +export default SavingsTransaction; diff --git a/apps/web/src/app/components/home/RewardsPanel/NetworkFeesRewards.tsx b/apps/web/src/app/components/home/RewardsPanel/NetworkFeesRewards.tsx index c19f32d23..9e9b84a46 100644 --- a/apps/web/src/app/components/home/RewardsPanel/NetworkFeesRewards.tsx +++ b/apps/web/src/app/components/home/RewardsPanel/NetworkFeesRewards.tsx @@ -25,9 +25,11 @@ import { useHasNetworkFees } from '@/store/reward/hooks'; import { useTransactionAdder } from '@/store/transactions/hooks'; import { useHasEnoughICX } from '@/store/wallet/hooks'; import { showMessageOnBeforeUnload } from '@/utils/messages'; -import { bnJs } from '@balancednetwork/xwagmi'; +import { XTransactionType, bnJs, getICONXTransactionInput } from '@balancednetwork/xwagmi'; +import { bnUSD } from '@/constants/tokens'; import { useSavingsXChainId } from '@/store/savings/hooks'; +import { CurrencyAmount } from '@balancednetwork/sdk-core'; import RewardsGrid from './RewardsGrid'; const NetworkFeesReward = ({ showGlobalTooltip }: { showGlobalTooltip: boolean }) => { @@ -53,6 +55,7 @@ const NetworkFeesReward = ({ showGlobalTooltip }: { showGlobalTooltip: boolean } const handleClaim = () => { window.addEventListener('beforeunload', showMessageOnBeforeUnload); + if (!account) return; bnJs .inject({ account }) @@ -63,6 +66,12 @@ const NetworkFeesReward = ({ showGlobalTooltip }: { showGlobalTooltip: boolean } { summary: t`Claimed network fees.`, pending: t`Claiming network fees...`, + type: XTransactionType.CLAIM_NETWORK_FEES, + input: getICONXTransactionInput( + account, + XTransactionType.CLAIM_NETWORK_FEES, + CurrencyAmount.fromRawAmount(bnUSD[1], 0), + ), }, ); toggleOpen(); diff --git a/apps/web/src/store/transactions/updater.tsx b/apps/web/src/store/transactions/updater.tsx index 94ca71323..735862f9d 100644 --- a/apps/web/src/store/transactions/updater.tsx +++ b/apps/web/src/store/transactions/updater.tsx @@ -37,7 +37,8 @@ export default function Updater(): null { tx.type === XTransactionType.DEPOSIT_ON_ICON || tx.type === XTransactionType.WITHDRAW_ON_ICON || tx.type === XTransactionType.BORROW_ON_ICON || - tx.type === XTransactionType.REPAY_ON_ICON, + tx.type === XTransactionType.REPAY_ON_ICON || + tx.type === XTransactionType.CLAIM_NETWORK_FEES, ), ); // biome-ignore lint/correctness/useExhaustiveDependencies: diff --git a/packages/xwagmi/src/xcall/types.ts b/packages/xwagmi/src/xcall/types.ts index 44b43f705..8db52aea7 100644 --- a/packages/xwagmi/src/xcall/types.ts +++ b/packages/xwagmi/src/xcall/types.ts @@ -58,6 +58,9 @@ export enum XTransactionType { SAVINGS_LOCK_BNUSD = 'savings_lock_bnusd', SAVINGS_UNLOCK_BNUSD = 'savings_unlock_bnusd', SAVINGS_CLAIM_REWARDS = 'savings_claim_rewards', + + // network fees + CLAIM_NETWORK_FEES = 'claim_network_fees', } export enum XMessageStatus { diff --git a/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx b/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx index 7e1a04866..c6c11142f 100644 --- a/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx +++ b/packages/xwagmi/src/xcall/zustand/useXMessageStore.tsx @@ -183,7 +183,8 @@ export const useXMessageStore = create()( xTransaction.type === XTransactionType.DEPOSIT_ON_ICON || xTransaction.type === XTransactionType.WITHDRAW_ON_ICON || xTransaction.type === XTransactionType.BORROW_ON_ICON || - xTransaction.type === XTransactionType.REPAY_ON_ICON + xTransaction.type === XTransactionType.REPAY_ON_ICON || + xTransaction.type === XTransactionType.CLAIM_NETWORK_FEES ) { return; } From 10e69180c5fcf52673173ca4cae9716b69ef7b57 Mon Sep 17 00:00:00 2001 From: hetfly Date: Tue, 22 Apr 2025 10:36:31 +0200 Subject: [PATCH 22/24] feat: add tracker links --- .../components/RecentActivity/HistoryItem.tsx | 16 +++++++++++++++- packages/xwagmi/src/xcall/utils.ts | 10 +++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index 60b210db2..1b235ebb2 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -1,5 +1,5 @@ import { MMTransaction } from '@/store/transactions/useMMTransactionStore'; -import { XTransaction, XTransactionType } from '@balancednetwork/xwagmi'; +import { XTransaction, XTransactionType, getTxTrackerLink } from '@balancednetwork/xwagmi'; import { motion } from 'framer-motion'; import React from 'react'; import BridgeTransaction from './transactions/BridgeTransaction'; @@ -21,6 +21,18 @@ interface HistoryItemProps { } const HistoryItem: React.FC = ({ transaction, isMMTransaction }) => { + const hash = transaction.id.split('/')[1] || transaction.id; + const xChainId = isMMTransaction(transaction) ? transaction.fromAmount.currency.xChainId : transaction.sourceChainId; + const trackerLink = getTxTrackerLink(hash, xChainId); + + const handleClick = () => { + if (trackerLink) { + window.open(trackerLink, '_blank'); + } + }; + + // console.log('kkk', trackerLink); + const renderContent = () => { if (isMMTransaction(transaction)) { return ; @@ -85,6 +97,8 @@ const HistoryItem: React.FC = ({ transaction, isMMTransaction animate={{ opacity: 1, y: 0, height: 'auto' }} transition={{ duration: 0.3, ease: 'easeOut' }} key={transaction.id} + onClick={handleClick} + style={{ cursor: trackerLink ? 'pointer' : 'default' }} > {renderContent()} diff --git a/packages/xwagmi/src/xcall/utils.ts b/packages/xwagmi/src/xcall/utils.ts index 5291c358c..08144de57 100644 --- a/packages/xwagmi/src/xcall/utils.ts +++ b/packages/xwagmi/src/xcall/utils.ts @@ -8,7 +8,7 @@ import { xTokenMap } from '@/constants/xTokens'; import { XChain, XToken } from '@/types'; import { uintToBytes } from '@/utils'; import { xChains } from '../constants/xChains'; -import { XTransactionInput, XTransactionType } from './types'; +import { XTransaction, XTransactionInput, XTransactionType } from './types'; export function getBytesFromNumber(value) { const hexString = value.toString(16).padStart(2, '0'); @@ -135,3 +135,11 @@ export const getICONXTransactionInput = ( }, }; }; + +export const getTxTrackerLink = (hash?: string, xChainId?: XChainId): string | undefined => { + if (hash && xChainId) { + return `${xChains.find(x => x.xChainId === xChainId)?.tracker.tx}/${hash}`; + } + + return undefined; +}; From 0c8c653de8550b3d9b832e75a557297e0d0f00ae Mon Sep 17 00:00:00 2001 From: hetfly Date: Tue, 29 Apr 2025 08:16:02 +0200 Subject: [PATCH 23/24] feat: improve recent activity ux --- .../components/RecentActivity/HistoryItem.tsx | 4 +- .../transactions/_styledComponents.ts | 45 ++++++++++++++----- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx index 1b235ebb2..1b7ff8714 100644 --- a/apps/web/src/app/components/RecentActivity/HistoryItem.tsx +++ b/apps/web/src/app/components/RecentActivity/HistoryItem.tsx @@ -31,8 +31,6 @@ const HistoryItem: React.FC = ({ transaction, isMMTransaction } }; - // console.log('kkk', trackerLink); - const renderContent = () => { if (isMMTransaction(transaction)) { return ; @@ -98,7 +96,7 @@ const HistoryItem: React.FC = ({ transaction, isMMTransaction transition={{ duration: 0.3, ease: 'easeOut' }} key={transaction.id} onClick={handleClick} - style={{ cursor: trackerLink ? 'pointer' : 'default' }} + style={{ cursor: trackerLink ? 'pointer' : 'default', pointerEvents: trackerLink ? 'auto' : 'none' }} > {renderContent()} diff --git a/apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts b/apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts index e2c48fbc0..8958009ac 100644 --- a/apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts +++ b/apps/web/src/app/components/RecentActivity/transactions/_styledComponents.ts @@ -1,32 +1,55 @@ import styled from 'styled-components'; -export const Container = styled.div` - display: flex; - justify-content: space-between; - align-items: center; -`; - -export const Details = styled.div` - flex-grow: 1; - margin-left: 12px; -`; - export const Title = styled.div` font-weight: bold; color: #FFFFFF; font-size: 14px; + transition: all 0.2s ease-in-out; + padding-right: 5px; + padding-left: 0; `; export const Amount = styled.div` color: ${({ theme }) => theme.colors.text1}; font-size: 14px; opacity: 0.9; + transition: all 0.2s ease-in-out; + padding-right: 5px; + padding-left: 0; `; export const Status = styled.div` color: ${({ theme }) => theme.colors.text}; font-size: 14px; text-align: right; + transition: all 0.2s ease-in-out; +`; + +export const Container = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + + &:hover { + ${Title} { + color: ${({ theme }) => theme.colors.primaryBright}; + padding-right: 0; + padding-left: 5px; + } + ${Amount} { + color: ${({ theme }) => theme.colors.primaryBright}; + padding-right: 0; + padding-left: 5px; + } + ${Status} { + color: ${({ theme }) => theme.colors.primaryBright}; + } + } +`; + +export const Details = styled.div` + flex-grow: 1; + margin-left: 12px; `; export const ElapsedTime = styled.div` From cba1862cf2e49f9df168c9b6528ccd2917429065 Mon Sep 17 00:00:00 2001 From: hetfly Date: Tue, 29 Apr 2025 08:24:04 +0200 Subject: [PATCH 24/24] feat: make suivision default tracker for sui --- packages/xwagmi/src/constants/xChains.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/xwagmi/src/constants/xChains.ts b/packages/xwagmi/src/constants/xChains.ts index b2c030c71..e831c9fa6 100644 --- a/packages/xwagmi/src/constants/xChains.ts +++ b/packages/xwagmi/src/constants/xChains.ts @@ -345,7 +345,7 @@ export const sui: XChain = { name: 'Sui', xChainId: 'sui', xChainType: 'SUI', - tracker: { tx: 'https://suiscan.xyz/mainnet/tx' }, + tracker: { tx: 'https://suivision.xyz/txblock' }, nativeCurrency: { decimals: 9, name: 'SUI',