Skip to content

Show tx history #669

New issue

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

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

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/ui/graphql.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"overwrite": true,
"schema": "https://chainsafe.squids.live/multix-arrow@v7/api/graphql",
"schema": "https://chainsafe.squids.live/multix-arrow@v8/api/graphql",
"documents": "src/**/*.graphql",
"generates": {
"src/gql/": {
Expand Down
1 change: 0 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
"@fontsource/jost": "^5.2.5",
"@mui/base": "^5.0.0-beta.70",
"@mui/material": "^6.4.10",
"@mui/styled-engine": "^6.4.9",
"@paraspell/sdk": "^8.9.0",
Expand Down
123 changes: 123 additions & 0 deletions packages/ui/src/components/Transactions/HistoricTransaction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import Paper from '@mui/material/Paper'
import { MultisigTxsByIdQuery } from '../../../types-and-hooks'
import { Box, styled } from '@mui/material'
import { TxStatus } from '../../types'
import {
HiOutlineCheckCircle as SuccessIncon,
HiOutlineMinusCircle as CancelledIcon,
HiOutlineXCircle as ErrorIcon
} from 'react-icons/hi2'
import { useEffect, useState } from 'react'
import { getCallDataFromChainPromise } from '../../utils/getCallDataFromChain'
import { useApi } from '../../contexts/ApiContext'
// import { JSONprint } from '../../utils/jsonPrint'
import CallInfo from '../CallInfo'
import { CallDataInfoFromChain } from '../../contexts/PendingTxContext'
import localizedFormat from 'dayjs/plugin/localizedFormat'
import dayjs from 'dayjs'
dayjs.extend(localizedFormat)

interface Props {
className?: string
tx: MultisigTxsByIdQuery['multisigTxes'][0]
}

export const HistoricTransaction = ({ className, tx }: Props) => {
const [res, setRes] = useState<CallDataInfoFromChain | undefined>()
const { api, client } = useApi()

useEffect(() => {
if (!api || !client) return

getCallDataFromChainPromise(
[
{
from: '',
hash: '',
info: {
approvals: [],
deposit: 0n,
depositor: '',
when: {
height: tx.blockNumber,
index: tx.extrinsicIndex
}
}
}
],
api,
client
)[0]
.then((res) => {
setRes(res)
})
.catch(console.error)
}, [api, client, tx.blockNumber, tx.extrinsicIndex])
const Icon =
tx.status === TxStatus.Cancelled
? CancelledIconStyled
: tx.status === TxStatus.Error
? ErrorIconStyled
: SuccessIconStyled

return (
<PaperStyled
className={className}
data-cy="container-pending-tx-item"
>
<StatusContainerStyled>
<Icon />
{/* {tx.status} */}
</StatusContainerStyled>
<StatusContainerStyled>
{(res?.timestamp && dayjs(res.timestamp).format('LL')) || ''}
</StatusContainerStyled>
{/* <pre>{JSONprint(res)}</pre> */}
{!!res && (
<CallInfo
aggregatedData={res}
withLink
isPplChainTx={false}
/>
)}
</PaperStyled>
)
}

const StatusContainerStyled = styled(Box)`
display: flex;
align-items: center;
margin-right: 1rem;
`

const PaperStyled = styled(Paper)`
display: flex;
flex-direction: column;
margin-bottom: 1rem;
padding: 1rem;

@media (min-width: ${({ theme }) => theme.breakpoints.values.sm}px) {
flex-direction: row;
margin-left: 0.5rem;
}
`
const CancelledIconStyled = styled(CancelledIcon)`
color: ${({ theme }) => theme.custom.identity.grey};
width: 1.5rem;
height: 1.5rem;
margin-right: 0.5rem;
`

const SuccessIconStyled = styled(SuccessIncon)`
color: ${({ theme }) => theme.custom.identity.green};
width: 1.5rem;
height: 1.5rem;
margin-right: 0.5rem;
`

const ErrorIconStyled = styled(ErrorIcon)`
color: ${({ theme }) => theme.custom.identity.red};
width: 1.5rem;
height: 1.5rem;
margin-right: 0.5rem;
`
155 changes: 155 additions & 0 deletions packages/ui/src/components/Transactions/History.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { Box, CircularProgress, Paper } from '@mui/material'
import { styled } from '@mui/material/styles'
import { useMultiProxy } from '../../contexts/MultiProxyContext'
import { MdOutlineFlare as FlareIcon } from 'react-icons/md'
import { useQueryTxHistory } from '../../hooks/useQueryTxHistory'
import { useMemo } from 'react'
import { getPubKeyFromAddress } from '../../utils/getPubKeyFromAddress'
import { useAccountId } from '../../hooks/useAccountId'
import { HistoricTransaction } from './HistoricTransaction'

interface Props {
className?: string
}

export const History = ({ className }: Props) => {
const { selectedMultiProxy } = useMultiProxy()
const multisigAddresses = useMemo(
() => selectedMultiProxy?.multisigs.map((m) => m.address) || [],
[selectedMultiProxy]
)
const multisigPubKeys = useMemo(
() => getPubKeyFromAddress(multisigAddresses),
[multisigAddresses]
)
const multisigIds = useAccountId(multisigPubKeys)
const { data, error, isLoading } = useQueryTxHistory({ multisigIds })

// interface TxParams {
// groupedTxs: AggGroupedByDate
// refreshFn: () => Promise<void>
// isPplChainTxs: boolean
// }

// const Transactions = ({ groupedTxs, refreshFn, isPplChainTxs }: TxParams) => {
// return (
// Object.entries(groupedTxs).length !== 0 &&
// Object.entries(groupedTxs).map(([date, aggregatedData]) => {
// return (
// <TransactionWrapper key={`${date}-${isPplChainTxs}`}>
// <DateContainerStyled data-cy="label-date">{date}</DateContainerStyled>
// {aggregatedData.map((agg, index) => {
// const { callData, info, from } = agg
// const { threshold, signatories } =
// getMultisigByAddress(from) ||
// ({ threshold: undefined, signatories: undefined } as MultisigAggregated)

// // if the "from" is not a multisig from the
// // currently selected multiProxy or we have no info
// if (!info || !threshold) {
// return null
// }

// const multisigSignatories = signatories || []
// // if the threshold is met, but the transaction is still not executed
// // it means we need one signtory to submit with asMulti
// // so any signatory should be able to approve (again)
// const neededSigners =
// info?.approvals.length >= threshold
// ? multisigSignatories
// : getDifference(multisigSignatories, info?.approvals)
// const possibleSigners = getIntersection(neededSigners, ownAddressList)
// const isProposer = !!info?.depositor && ownAddressList.includes(info.depositor)

// // if we have the proposer in the extension it can always reject the transaction
// if (isProposer) {
// possibleSigners.push(info.depositor)
// }

// return (
// <Transaction
// key={`${index}-${callData}`}
// aggregatedData={agg}
// isProposer={isProposer}
// onSuccess={refreshFn}
// possibleSigners={possibleSigners}
// multisigSignatories={multisigSignatories}
// threshold={threshold}
// isPplChainTx={isPplChainTxs}
// />
// )
// })}
// </TransactionWrapper>
// )
// })
// )
// }
return (
<Box className={className}>
{isLoading && (
<LoaderStyled data-cy="loader-transaction-list">
<CircularProgress />
</LoaderStyled>
)}
{(!data || data.multisigTxes.length === 0) && !isLoading && (
<NoCallWrapperStyled>
<FlareIconStyled size={24} />
<div>No past transaction found!</div>
</NoCallWrapperStyled>
)}
{!!data &&
data.multisigTxes.length > 0 &&
!isLoading &&
data.multisigTxes
.sort((a, b) => b.blockNumber - a.blockNumber)
.slice(0, 10)
.map((tx) => {
return (
<HistoricTransaction
key={tx.id}
className={className}
tx={tx}
/>
)
})}
{!!error && <pre>{JSON.stringify(error, null, 2)}</pre>}
{/* {Object.entries(tx).length === 0 &&
Object.entries(pplTx).length === 0 &&
!isLoading && (
<NoCallWrapperStyled>
<FlareIconStyled size={24} />
<div>You&apos;re all set!</div>
</NoCallWrapperStyled>
)}
<Transactions
groupedTxs={tx}
isPplChainTxs={false}
refreshFn={refresh}
/>
<Transactions
groupedTxs={pplTx}
isPplChainTxs={true}
refreshFn={refreshPpl}
/> */}
</Box>
)
}

const FlareIconStyled = styled(FlareIcon)`
font-size: 3rem;
margin-bottom: 1rem;
`

const NoCallWrapperStyled = styled(Paper)`
background-color: ${({ theme }) => theme.custom.background.primary};
display: flex;
flex-direction: column;
align-content: center;
align-items: center;
padding: 2rem;
`

const LoaderStyled = styled(Box)`
display: flex;
justify-content: center;
`
3 changes: 1 addition & 2 deletions packages/ui/src/components/Transactions/TransactionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { MultisigAggregated, useMultiProxy } from '../../contexts/MultiProxyCont
import { getDifference, getIntersection } from '../../utils/arrayUtils'
import { useAccounts } from '../../contexts/AccountsContext'
import { MdOutlineFlare as FlareIcon } from 'react-icons/md'
import Transaction from './Transaction'
import Transaction from './Transactions'

interface Props {
className?: string
Expand Down Expand Up @@ -84,7 +84,6 @@ const TransactionList = ({ className }: Props) => {
}
return (
<Box className={className}>
<h3>Transactions</h3>
{isLoadingPendingTxs && (
<LoaderStyled data-cy="loader-transaction-list">
<CircularProgress />
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export interface NetworkInfo {
genesisHash?: string
}

export const HTTP_GRAPHQL_URL = `https://chainsafe.squids.live/multix-arrow@v7/api/graphql`
export const HTTP_GRAPHQL_URL = `https://chainsafe.squids.live/multix-arrow@v8/api/graphql`

export const PAYMENT_INFO_ACCOUNT = '5CXQZrh1MSgnGGCdJu3tqvRfCv7t5iQXGGV9UKotrbfhkavs'

Expand Down
Loading
Loading