diff --git a/src/app/components/rareSatAsset/rareSatAsset.tsx b/src/app/components/rareSatAsset/rareSatAsset.tsx
new file mode 100644
index 000000000..27c763650
--- /dev/null
+++ b/src/app/components/rareSatAsset/rareSatAsset.tsx
@@ -0,0 +1,85 @@
+import RareSatIcon from '@components/rareSatIcon/rareSatIcon';
+import OrdinalImage from '@screens/ordinals/ordinalImage';
+import { Inscription } from '@secretkeylabs/xverse-core';
+import { BundleItem } from '@utils/rareSats';
+import styled from 'styled-components';
+
+const Container = styled.div`
+ width: 100%;
+ height: 100%;
+`;
+
+const InscriptionContainer = styled.div`
+ width: 100%;
+ height: 100%;
+ position: relative;
+ border-radius: 8px;
+ overflow: hidden;
+`;
+
+const RareSatIconContainer = styled.div<{ isGallery: boolean }>((props) => ({
+ display: 'flex',
+ position: 'absolute',
+ zIndex: 1,
+ left: props.isGallery ? 20 : 8,
+ top: props.isGallery ? 20 : 8,
+}));
+
+const RareSatsContainer = styled.div((props) => ({
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ width: '100%',
+ aspectRatio: '1',
+ overflow: 'hidden',
+ position: 'relative',
+ backgroundColor: props.theme.colors.elevation1,
+ borderRadius: 8,
+}));
+
+const DynamicSizeContainer = styled.div<{ isCollage: boolean }>((props) => ({
+ width: props.isCollage ? '40%' : '50%',
+ height: props.isCollage ? '40%' : '50%',
+}));
+
+interface Props {
+ item: BundleItem;
+ isCollage?: boolean;
+}
+
+function RareSatAsset({ item, isCollage = false }: Props) {
+ const isGallery: boolean = document.documentElement.clientWidth > 360;
+ const isInscription = item.type === 'inscription' || item.type === 'inscribed-sat';
+
+ return (
+
+ {isInscription ? (
+
+ {!isCollage && !!item.rarity_ranking && item.rarity_ranking !== 'COMMON' && (
+
+
+
+ )}
+
+
+ ) : (
+
+
+
+
+
+ )}
+
+ );
+}
+
+export default RareSatAsset;
diff --git a/src/app/screens/signPsbtRequest/bundleItemsComponent.tsx b/src/app/screens/signPsbtRequest/bundleItemsComponent.tsx
new file mode 100644
index 000000000..82d0de52d
--- /dev/null
+++ b/src/app/screens/signPsbtRequest/bundleItemsComponent.tsx
@@ -0,0 +1,219 @@
+import Eye from '@assets/img/createPassword/Eye.svg';
+import Cross from '@assets/img/dashboard/X.svg';
+import IconOrdinal from '@assets/img/transactions/ordinal.svg';
+import RareSatAsset from '@components/rareSatAsset/rareSatAsset';
+import { animated, useSpring } from '@react-spring/web';
+import { getTruncatedAddress } from '@utils/helper';
+import { BundleItem, getBundleItemSubText } from '@utils/rareSats';
+import { useState } from 'react';
+import { useTranslation } from 'react-i18next';
+import styled from 'styled-components';
+
+const Container = styled.div((props) => ({
+ display: 'flex',
+ flexDirection: 'column',
+ background: props.theme.colors.elevation1,
+ borderRadius: 12,
+ padding: '16px 16px',
+ justifyContent: 'center',
+ marginBottom: 12,
+}));
+
+const RecipientTitleText = styled.h1((props) => ({
+ ...props.theme.body_medium_m,
+ color: props.theme.colors.white_200,
+ marginBottom: 10,
+}));
+
+const RowContainer = styled.div({
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+});
+
+const TransparentButton = styled.button({
+ background: 'transparent',
+ display: 'flex',
+ alignItems: 'center',
+ marginLeft: 10,
+});
+
+const Icon = styled.img((props) => ({
+ marginRight: props.theme.spacing(4),
+ width: 32,
+ height: 32,
+ borderRadius: 30,
+}));
+
+const TitleText = styled.h1((props) => ({
+ ...props.theme.body_medium_m,
+ color: props.theme.colors.white_200,
+}));
+
+const ValueText = styled.h1((props) => ({
+ ...props.theme.body_medium_m,
+ color: props.theme.colors.white_0,
+}));
+
+const SubValueText = styled.h1((props) => ({
+ ...props.theme.body_m,
+ fontSize: 12,
+ color: props.theme.colors.white_400,
+}));
+
+const InscriptionText = styled.h1((props) => ({
+ ...props.theme.body_medium_m,
+ fontSize: 21,
+ marginTop: 24,
+ textAlign: 'center',
+ color: props.theme.colors.white[0],
+ overflowWrap: 'break-word',
+ wordWrap: 'break-word',
+ wordBreak: 'break-word',
+}));
+
+const ColumnContainer = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ justifyContent: 'flex-end',
+ alignItems: 'flex-end',
+ marginTop: 12,
+});
+
+const CrossContainer = styled.div({
+ display: 'flex',
+ marginTop: 10,
+ justifyContent: 'flex-end',
+ alignItems: 'flex-end',
+});
+
+const OrdinalOuterImageContainer = styled.div({
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 2,
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+});
+
+const OrdinalImageContainer = styled.div({
+ width: '50%',
+});
+
+const OrdinalBackgroundContainer = styled(animated.div)({
+ width: '100%',
+ height: '100%',
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ position: 'fixed',
+ zIndex: 10,
+ background: 'rgba(18, 21, 30, 0.8)',
+ backdropFilter: 'blur(16px)',
+ padding: 16,
+ display: 'flex',
+ flexDirection: 'column',
+});
+
+const EyeIcon = styled.img({
+ width: 20,
+ height: 20,
+});
+
+interface Props {
+ item: BundleItem;
+ userReceivesOrdinal: boolean;
+}
+function BundleItemsComponent({ item, userReceivesOrdinal }: Props) {
+ const { t } = useTranslation('translation');
+ const [showOrdinal, setShowOrdinal] = useState(false);
+ const styles = useSpring({
+ from: {
+ opacity: 0,
+ y: 24,
+ },
+ to: {
+ y: 0,
+ opacity: 1,
+ },
+ delay: 100,
+ });
+ const onButtonClick = () => {
+ setShowOrdinal(true);
+ };
+
+ const onCrossClick = () => {
+ setShowOrdinal(false);
+ };
+ const getItemId = () => {
+ if (item.type === 'inscription') {
+ return item.inscription.id;
+ }
+ if (item.type === 'inscribed-sat' || item.type === 'rare-sat') {
+ return item.number;
+ }
+ return '';
+ };
+ const itemSubText = getBundleItemSubText({
+ satType: item.type,
+ rareSatsType: item.rarity_ranking as any,
+ });
+ const getDetail = () => {
+ if (item.type === 'inscription' || item.type === 'inscribed-sat') {
+ return item.inscription.content_type;
+ }
+ return itemSubText;
+ };
+ const getTitle = () => {
+ if (item.type === 'inscription') {
+ return t('COMMON.INSCRIPTION');
+ }
+ if (item.type === 'inscribed-sat') {
+ return t('RARE_SATS.INSCRIBED_SAT');
+ }
+ return t('RARE_SATS.RARE_SAT');
+ };
+ return (
+ <>
+ {showOrdinal && (
+
+
+
+
+
+
+
+
+
+
+ {`${getTitle()} ${getItemId()} `}
+
+
+ )}
+
+
+ {userReceivesOrdinal
+ ? t('CONFIRM_TRANSACTION.YOU_WILL_RECEIVE')
+ : t('CONFIRM_TRANSACTION.YOU_WILL_TRANSFER')}
+
+
+
+ {getTitle()}
+
+
+ {getTruncatedAddress(String(getItemId()))}
+
+
+
+
+ {getDetail()}
+
+
+
+ >
+ );
+}
+
+export default BundleItemsComponent;
diff --git a/src/app/screens/signPsbtRequest/index.tsx b/src/app/screens/signPsbtRequest/index.tsx
index f6be05754..8caae6b05 100644
--- a/src/app/screens/signPsbtRequest/index.tsx
+++ b/src/app/screens/signPsbtRequest/index.tsx
@@ -5,19 +5,17 @@ import { delay } from '@common/utils/ledger';
import AccountHeaderComponent from '@components/accountHeader';
import BottomModal from '@components/bottomModal';
import ActionButton from '@components/button';
-import SatsBundle from '@components/confirmBtcTransactionComponent/bundle';
import InputOutputComponent from '@components/confirmBtcTransactionComponent/inputOutputComponent';
import InfoContainer from '@components/infoContainer';
import LedgerConnectionView from '@components/ledger/connectLedgerView';
import RecipientComponent from '@components/recipientComponent';
import TransactionDetailComponent from '@components/transactionDetailComponent';
import useBtcClient from '@hooks/useBtcClient';
-import useDetectOrdinalInSignPsbt, { InputsBundle } from '@hooks/useDetectOrdinalInSignPsbt';
+import useDetectOrdinalInSignPsbt from '@hooks/useDetectOrdinalInSignPsbt';
import useSignPsbtTx from '@hooks/useSignPsbtTx';
import useWalletSelector from '@hooks/useWalletSelector';
import Transport from '@ledgerhq/hw-transport-webusb';
import {
- Bundle,
getBtcFiatEquivalent,
parsePsbt,
psbtBase64ToHex,
@@ -26,6 +24,7 @@ import {
Transport as TransportType,
} from '@secretkeylabs/xverse-core';
import { isLedgerAccount } from '@utils/helper';
+import { BundleItem, convertV2ToV1Bundle } from '@utils/rareSats';
import BigNumber from 'bignumber.js';
import { decodeToken } from 'jsontokens';
import { useEffect, useMemo, useState } from 'react';
@@ -35,6 +34,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import { MoonLoader } from 'react-spinners';
import { SignTransactionOptions } from 'sats-connect';
import styled from 'styled-components';
+import BundleItemsComponent from './bundleItemsComponent';
const OuterContainer = styled.div`
display: flex;
@@ -110,9 +110,6 @@ function SignPsbtRequest() {
const { t: signatureRequestTranslate } = useTranslation('translation', {
keyPrefix: 'SIGNATURE_REQUEST',
});
- const { t: tCommon } = useTranslation('translation', {
- keyPrefix: 'COMMON',
- });
const [expandInputOutputView, setExpandInputOutputView] = useState(false);
const { payload, confirmSignPsbt, cancelSignPsbt, getSigningAddresses } = useSignPsbtTx();
const [isSigning, setIsSigning] = useState(false);
@@ -142,7 +139,7 @@ function SignPsbtRequest() {
const handleOrdinalAndOrdinalInfo = useDetectOrdinalInSignPsbt();
const [isLoading, setIsLoading] = useState(true);
const [userReceivesOrdinal, setUserReceivesOrdinal] = useState(false);
- const [bundleItemsData, setBundleItemsData] = useState();
+ const [bundleItemsData, setBundleItemsData] = useState([]);
const signingAddresses = useMemo(
() => getSigningAddresses(payload.inputsToSign),
[payload.inputsToSign],
@@ -189,7 +186,7 @@ function SignPsbtRequest() {
const checkIfUserReceivesOrdinal = async () => {
try {
const result = await handleOrdinalAndOrdinalInfo(parsedPsbt);
- setBundleItemsData(result.bundleItemsData);
+ setBundleItemsData(convertV2ToV1Bundle(result.bundleItemsData));
setUserReceivesOrdinal(result.userReceivesOrdinal);
} catch {
navigate('/tx-status', {
@@ -387,21 +384,15 @@ function SignPsbtRequest() {
{t('REVIEW_TRANSACTION')}
{!payload.broadcast && }
- {Array.isArray(bundleItemsData) &&
- bundleItemsData.map((bundle, index) => (
- (
+
))}
-
{
? `${t('COMMON.COMBO')}`
: `${getRareSatsLabelByType(satributes[0])} ${getRareSatsLabelByType(satributes[1])}`;
};
+
+// remove later when we fix sign psbt screen
+export type BundleItem =
+ | {
+ type: 'rare-sat';
+ rarity_ranking: RodarmorRareSatsType;
+ number: string;
+ }
+ | {
+ type: 'inscribed-sat';
+ rarity_ranking: RodarmorRareSatsType;
+ number: string;
+ inscription: {
+ id: string;
+ content_type: string;
+ };
+ }
+ | {
+ type: 'inscription';
+ rarity_ranking: RodarmorRareSatsType;
+ inscription: {
+ id: string;
+ content_type: string;
+ };
+ }
+ | {
+ type: 'unknown';
+ rarity_ranking: 'unknown';
+ };
+
+// remove later when we fix sign psbt screen
+export const convertV2ToV1Bundle = (v2: any): BundleItem[] => {
+ const bundleItems: BundleItem[] = [];
+ v2.forEach((item) => {
+ item.satRanges.forEach((satRange) => {
+ satRange.inscriptions.forEach((inscription) => {
+ bundleItems.push({
+ type: 'inscription',
+ rarity_ranking: 'COMMON',
+ inscription,
+ });
+ });
+ });
+ });
+ return bundleItems;
+};
+
+// remove later when we fix sign psbt screen
+export const getBundleItemSubText = ({
+ satType,
+ rareSatsType,
+}: {
+ satType: any;
+ rareSatsType: RareSatsType;
+}) =>
+ ({
+ inscription: t('COMMON.INSCRIPTION'),
+ 'rare-sat': t('RARE_SATS.SAT_TYPES.RARE_SAT', {
+ type: getRareSatsLabelByType(rareSatsType ?? 'unknown'),
+ }),
+ 'inscribed-sat': t('RARE_SATS.SAT_TYPES.INSCRIBED_RARE_SAT', {
+ type: getRareSatsLabelByType(rareSatsType ?? 'unknown'),
+ }),
+ unknown: t('RARE_SATS.SAT_TYPES.UNKNOWN_RARE_SAT'),
+ }[satType]);