Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/l1 l2 #348

Merged
merged 15 commits into from
Jun 19, 2024
Merged
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 src/components/LiteTransactionList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const LiteTransactionList: React.FC<{
>
{item.transactionHash}
</AddressText>
{item.isRgbTransaction && <RGBPP transaction={item} />}
{item.isRgbTransaction && item.rgbTransferStep && <RGBPP transaction={item} />}
</div>
</td>
{isPendingListActive ? null : (
Expand Down
24 changes: 3 additions & 21 deletions src/components/RGBPP/index.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,15 @@
import { useMemo, useState } from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Transaction } from '../../models/Transaction'
import SimpleModal from '../Modal'
import SimpleButton from '../SimpleButton'
import TransactionRGBPPDigestModal from '../TransactionItem/TransactionRGBPPDigestModal'
import styles from './styles.module.scss'
import { TransactionLeapDirection } from './types'

const RGBPP = ({ transaction }: { transaction: Transaction }) => {
const [showModal, setShowModal] = useState(false)
const { t } = useTranslation()

const direction = useMemo(() => {
// FIXME: should be from API because inputs/outputs are paginated
const inputCellCount = transaction.displayInputs.filter(c => c.rgbInfo).length
const outputCellCount = transaction.displayOutputs.filter(c => c.rgbInfo).length
if (inputCellCount === outputCellCount) {
return TransactionLeapDirection.NONE
}
if (inputCellCount > outputCellCount) {
return TransactionLeapDirection.OUT
}
return TransactionLeapDirection.IN
}, [transaction])

return (
<div>
<SimpleButton
Expand All @@ -32,15 +18,11 @@ const RGBPP = ({ transaction }: { transaction: Transaction }) => {
}}
>
<div className={styles.rgbpp}>
<span>{t('transaction.view_rgbpp_digest')}</span>
<span>RGB++: {t(`rgbpp.transaction.step.${transaction.rgbTransferStep}`)}</span>
</div>
</SimpleButton>
<SimpleModal isShow={showModal} setIsShow={setShowModal}>
<TransactionRGBPPDigestModal
onClickClose={() => setShowModal(false)}
hash={transaction.transactionHash}
leapDirection={direction}
/>
<TransactionRGBPPDigestModal onClickClose={() => setShowModal(false)} hash={transaction.transactionHash} />
</SimpleModal>
</div>
)
Expand Down
1 change: 1 addition & 0 deletions src/components/RGBPP/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export enum TransactionLeapDirection {
NONE = 'none',
IN = 'in',
OUT = 'out',
WITH_IN_BTC = 'with_in_btc',
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Tooltip } from 'antd'
import { useQuery } from '@tanstack/react-query'
import BigNumber from 'bignumber.js'
import { ReactComponent as CopyIcon } from '../../../assets/copy_icon.svg'
import { ReactComponent as RedirectIcon } from '../../../assets/redirect-icon.svg'
import { TransactionRGBPPDigestTransfer } from './TransactionRGBPPDigestTransfer'
import { useSetToast } from '../../Toast'
import { explorerService, LiteTransfer } from '../../../services/ExplorerService'
import SmallLoading from '../../Loading/SmallLoading'
import { TransactionLeapDirection } from '../../RGBPP/types'
import { LiteTransfer, RGBDigest } from '../../../services/ExplorerService'
import SimpleButton from '../../SimpleButton'
import EllipsisMiddle from '../../EllipsisMiddle'
import styles from './styles.module.scss'
import AddressText from '../../AddressText'
import { useIsMobile } from '../../../hooks'
import { Link } from '../../Link'
import config from '../../../config'
import SmallLoading from '../../Loading/SmallLoading'

export const TransactionRGBPPDigestContent = ({
leapDirection,
hash,
digest,
isFetched,
}: {
leapDirection: TransactionLeapDirection
hash: string
digest?: RGBDigest
isFetched: boolean
}) => {
const { t } = useTranslation()
const setToast = useSetToast()
const isMobile = useIsMobile()

const { data, isFetched } = useQuery(['rgb-digest', hash], () => explorerService.api.fetchRGBDigest(hash))

const transfers = useMemo(() => {
const m = new Map<string, LiteTransfer.Transfer[]>()
data?.data.transfers?.forEach(tf => {
digest?.transfers.forEach(tf => {
const list = m.get(tf.address) || []
tf.transfers.forEach(i => {
let asset: LiteTransfer.Transfer | undefined
Expand Down Expand Up @@ -84,7 +81,7 @@ export const TransactionRGBPPDigestContent = ({
m.set(tf.address, list)
})
return m
}, [data?.data.transfers])
}, [digest?.transfers])

if (!isFetched) {
return (
Expand All @@ -93,17 +90,17 @@ export const TransactionRGBPPDigestContent = ({
</div>
)
}
if (!data) {
if (!digest) {
return <div className={styles.noRecords}>{t('transaction.no_records')}</div>
}

return (
<div className={styles.content}>
{data.data.commitment ? (
{digest.commitment ? (
<div className={styles.transactionInfo}>
<div className={styles.txid}>
<span>{t('address.seal_tx_on_bitcoin')}</span>
{data.data.txid && (
{digest.txid && (
<>
<AddressText
ellipsisMiddle={!isMobile}
Expand All @@ -113,32 +110,27 @@ export const TransactionRGBPPDigestContent = ({
className={styles.address}
style={{ overflow: 'hidden' }}
>
{data.data.txid}
{digest.txid}
</AddressText>
<Link to={`${config.BITCOIN_EXPLORER}/tx/${data.data.txid}`} className={styles.action}>
<Link to={`${config.BITCOIN_EXPLORER}/tx/${digest.txid}`} className={styles.action}>
<RedirectIcon />
</Link>
</>
)}
</div>
<div className={styles.btcConfirmationsAndDirection}>
{typeof data.data.confirmations === 'number' && (
<span className={styles.blockConfirm}>({data.data.confirmations} Confirmations on Bitcoin)</span>
{typeof digest.confirmations === 'number' && (
<span className={styles.blockConfirm}>({digest.confirmations} Confirmations on Bitcoin)</span>
)}
{leapDirection !== TransactionLeapDirection.NONE ? (
<Tooltip placement="top" title={t(`address.leap_${leapDirection}_tip`)}>
<span className={styles.leap}>{t(`address.leap_${leapDirection}`)}</span>
</Tooltip>
) : null}
</div>
<div className={styles.commitment}>
<span>Commitment:</span>
<div style={{ width: '64ch', minWidth: '20ch' }}>
<EllipsisMiddle text={data.data.commitment} className={styles.commitmentText} />
<EllipsisMiddle text={digest.commitment} className={styles.commitmentText} />
<SimpleButton
className={styles.action}
onClick={() => {
navigator.clipboard.writeText(data.data.commitment)
navigator.clipboard.writeText(digest.commitment)
setToast({ message: t('common.copied') })
}}
>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,49 @@
import { useTranslation } from 'react-i18next'
import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import styles from './styles.module.scss'
import CloseIcon from '../../../assets/modal_close.png'
import { TransactionRGBPPDigestContent } from './TransactionRGBPPDigestContent'
import { explorerService } from '../../../services/ExplorerService'
import { TransactionLeapDirection } from '../../RGBPP/types'

const TransactionRGBPPDigestModal = ({
hash,
leapDirection,
onClickClose,
}: {
onClickClose: Function
hash: string
leapDirection: TransactionLeapDirection
}) => {
const TransactionRGBPPDigestModal = ({ hash, onClickClose }: { onClickClose: Function; hash: string }) => {
const { t } = useTranslation()

return (
<div className={styles.container}>
<div className={styles.header}>
<span className={styles.left}>{t('address.transaction_rgbpp_digest')}</span>
<button onClick={() => onClickClose()} type="button" className={styles.buttonClose}>
<img src={CloseIcon} alt="close icon" className={styles.closeIcon} />
</button>
const { data, isFetched } = useQuery(['rgb-digest', hash], () => explorerService.api.fetchRGBDigest(hash))

const direction = useMemo(() => {
switch (data?.data.leapDirection) {
case 'in':
return TransactionLeapDirection.IN
case 'leapoutBTC':
return TransactionLeapDirection.OUT
case 'withinBTC':
return TransactionLeapDirection.WITH_IN_BTC
default:
return TransactionLeapDirection.NONE
}
}, [data])

if (isFetched && data?.data) {
return (
<div className={styles.container}>
<div className={styles.header}>
<div className={styles.left}>
<span>{t('address.transaction_rgbpp_digest')}</span>
{direction !== TransactionLeapDirection.NONE && (
<span className={styles.leap}>{t(`address.leap_${direction}`)}</span>
)}
</div>
<button onClick={() => onClickClose()} type="button" className={styles.buttonClose}>
<img src={CloseIcon} alt="close icon" className={styles.closeIcon} />
</button>
</div>
<TransactionRGBPPDigestContent hash={hash} digest={data?.data} isFetched={isFetched} />
</div>
<TransactionRGBPPDigestContent leapDirection={leapDirection} hash={hash} />
</div>
)
)
}
return null
}

export default TransactionRGBPPDigestModal
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
.header {
display: flex;
justify-content: space-between;
margin-bottom: 10px;

.left {
display: flex;
gap: 8px;
align-items: center;
}

> span {
font-family: Roboto, sans-serif;
Expand Down Expand Up @@ -71,7 +78,9 @@

.txid,
.commitment {
max-width: 100%;
display: flex;
flex: 1 1;
flex-shrink: 1;
overflow: hidden;
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
Expand Down
2 changes: 1 addition & 1 deletion src/components/TransactionItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ const TransactionItem = ({
>
{transaction.transactionHash}
</AddressText>
{transaction.isRgbTransaction && <RGBPP transaction={transaction} />}
{transaction.isRgbTransaction && transaction.rgbTransferStep && <RGBPP transaction={transaction} />}
</div>
<div className={styles.right}>
<Time tx={isBlock ? undefined : transaction} />
Expand Down
7 changes: 6 additions & 1 deletion src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@
"tx_on_bitcoin": "Tx On Bitcoin",
"leap_in": "L1 → L2",
"leap_out": "L2 → L1",
"leap_with_in_btc": "L1 → L1",
"leap_in_tip": "This RGB++ transaction is marked as \"L1 → L2\", indicating that the number of cells with RGB++ lock in the output exceeds the number of cells with RGB++ lock in the input.",
"leap_out_tip": "This RGB++ transaction is marked as \"L2 → L1\", indicating that the number of cells with RGB++ lock in the input exceeds the number of cells with RGB++ lock in the output.",
"view_in_btc_explorer": "View in Bitcoin Explorer",
Expand Down Expand Up @@ -453,7 +454,11 @@
"btc_txid": "BTC TXID",
"cell": "Cell",
"cells": "Cells",
"view_on_bitcoin_explorer": "View on Bitcoin Explorer"
"view_on_bitcoin_explorer": "View on Bitcoin Explorer",
"step": {
"isomorphic": "Isomorphic",
"unlock": "Unlock"
}
},
"amount": "Amount"
},
Expand Down
7 changes: 6 additions & 1 deletion src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@
"tx_on_bitcoin": "Tx On Bitcoin",
"leap_in": "L1 → L2",
"leap_out": "L2 → L1",
"leap_with_in_btc": "L1 → L1",
"leap_in_tip": "本笔 RGB++ 交易被标记为\"L1 → L2\",即存在一般 CKB 资产转化为 RGB++ 资产。",
"leap_out_tip": "本笔 RGB++ 交易被标记为\"L2 → L1\",即存在 RGB++ 资产转化为一般 CKB 资产。",
"view_in_btc_explorer": "在 Bitcoin 浏览器查看",
Expand Down Expand Up @@ -453,7 +454,11 @@
"btc_txid": "BTC 交易",
"cell": "Cell",
"cells": "Cells",
"view_on_bitcoin_explorer": "在 Bitcoin 浏览器查看"
"view_on_bitcoin_explorer": "在 Bitcoin 浏览器查看",
"step": {
"isomorphic": "同构",
"unlock": "解锁"
}
},
"amount": "数量"
},
Expand Down
1 change: 1 addition & 0 deletions src/models/Transaction/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface Transaction {
displayOutputs: Cell[]
liveCellChanges: string
capacityInvolved: string
rgbTransferStep: string | null
txStatus: string
detailedMessage: string
bytes: number
Expand Down
2 changes: 1 addition & 1 deletion src/pages/RGBPP/TransactionList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const RGBPPTransactionList = () => {
leapDirection = TransactionLeapDirection.IN
}

if (tx.leapDirection === 'out') {
if (tx.leapDirection === '') {
leapDirection = TransactionLeapDirection.OUT
}

Expand Down
31 changes: 20 additions & 11 deletions src/pages/Transaction/TransactionComp/RGBDigestComp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export const RGBDigestComp = ({ hash, txid }: { hash: string; txid?: string }) =
: undefined,
)

const { data: rgbDigest, isFetched: isDigestFetched } = useQuery(['rgb-digest', hash], () =>
explorerService.api.fetchRGBDigest(hash),
)

const { data: displayInputs } = useQuery(
['transaction_inputs', hash, 1, 10],
async () => {
Expand Down Expand Up @@ -64,27 +68,32 @@ export const RGBDigestComp = ({ hash, txid }: { hash: string; txid?: string }) =
}, [displayInputs, displayOutputs])

const direction = useMemo(() => {
// FIXME: should be from API because inputs/outputs are paginated
const inputCellCount = displayInputs.data.filter(c => c.rgbInfo).length
const outputCellCount = displayOutputs.data.filter(c => c.rgbInfo).length
if (inputCellCount === outputCellCount) {
return TransactionLeapDirection.NONE
switch (rgbDigest?.data.leapDirection) {
case 'in':
return TransactionLeapDirection.IN
case 'leapoutBTC':
return TransactionLeapDirection.OUT
case 'withinBTC':
return TransactionLeapDirection.WITH_IN_BTC
default:
return TransactionLeapDirection.NONE
}
if (inputCellCount > outputCellCount) {
return TransactionLeapDirection.OUT
}
return TransactionLeapDirection.IN
}, [displayInputs, displayOutputs])
}, [rgbDigest])

return (
<>
<Card className={styles.transactionHeader}>
<div className={styles.headerContent}>
<p>{t('transaction.rgb_digest')} </p>
{direction !== TransactionLeapDirection.NONE && (
<span className={styles.leap}>{t(`address.leap_${direction}`)}</span>
)}
</div>
</Card>
<Card className={styles.digestContent}>
<TransactionRGBPPDigestContent hash={hash} leapDirection={direction} />
{rgbDigest && (
<TransactionRGBPPDigestContent hash={hash} digest={rgbDigest?.data} isFetched={isDigestFetched} />
)}
<div className={styles.btcTxContent}>
{isBtcTxLoading ? <SmallLoading /> : null}
{btcTx?.vout.some(v => v.scriptPubKey.asm.includes('OP_RETURN')) ? (
Expand Down
Loading