diff --git a/src/app/components/Charts/StockChartTemplate.tsx b/src/app/components/Charts/StockChartTemplate.tsx
index d5d183d03..ff70d9de8 100644
--- a/src/app/components/Charts/StockChartTemplate.tsx
+++ b/src/app/components/Charts/StockChartTemplate.tsx
@@ -154,7 +154,6 @@ export function StockChartTemplate({
tooltip: {
split: false,
useHTML: true,
- xDateFormat: '%a %e, %b %Y (UTC)',
headerFormat: `
{point.key} |
diff --git a/src/app/components/NFTPreview/index.tsx b/src/app/components/NFTPreview/index.tsx
index c43f06f88..48a29532b 100644
--- a/src/app/components/NFTPreview/index.tsx
+++ b/src/app/components/NFTPreview/index.tsx
@@ -23,6 +23,8 @@ import audioPause from './audio-pause.svg';
import audioPlay from './audio-play.svg';
import Link from '@zeit-ui/react-icons/link';
import { Link as ALink } from 'app/components/Link/Loadable';
+import { formatString } from 'utils';
+import { Tag } from '@cfxjs/antd';
const epiKProtocolKnowledgeBadge =
'cfx:acev4c2s2ttu3jzxzsd4a2hrzsa4pfc3f6f199y5mk';
@@ -181,10 +183,14 @@ export const NFTPreview = React.memo(
contractAddress,
tokenId,
type = 'preview',
+ amount = 0,
+ owner = '',
}: {
contractAddress?: string;
tokenId?: number | string;
type?: 'preview' | 'card' | 'primary';
+ amount?: number;
+ owner?: string;
}) => {
const { t, i18n } = useTranslation();
const lang = i18n.language.includes('zh') ? 'zh' : 'en';
@@ -226,28 +232,35 @@ export const NFTPreview = React.memo(
return (
- {imageUri ? (
- contractAddress === epiKProtocolKnowledgeBadge ? (
-
-
-
+
+ {imageUri ? (
+ contractAddress === epiKProtocolKnowledgeBadge ? (
+
+
+
+ ) : (
+
+ )
+ ) : isFirstTime ? (
+
) : (
-
- )
- ) : isFirstTime ? (
-
- ) : (
-
- )}
+ )}
+ {!!amount && (
+
+ x{amount}
+
+ )}
+
@@ -268,6 +281,15 @@ export const NFTPreview = React.memo(
{t(translations.general.table.token.view)}
+ {owner && (
+ <>
+
+
+ {t(translations.nftChecker.owner)}:{' '}
+ {formatString(owner, 'address')}
+
+ >
+ )}
@@ -483,6 +505,18 @@ const NFTCard = styled.div`
}
}
+ .nft-container {
+ position: relative;
+
+ .nft-amount {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ margin: 0;
+ padding: 0 5px;
+ }
+ }
+
.info {
padding: 8px 10px 10px;
font-size: 12px;
diff --git a/src/app/components/StatsCard/NetworkPie.tsx b/src/app/components/StatsCard/NetworkPie.tsx
index 9f23f5b6b..c92801f89 100644
--- a/src/app/components/StatsCard/NetworkPie.tsx
+++ b/src/app/components/StatsCard/NetworkPie.tsx
@@ -13,6 +13,9 @@ export function NetworkPie({ data }) {
title: {
text: '',
},
+ credits: {
+ enabled: false,
+ },
height: 9 / 21 / 100 + '%',
tooltip: {
outside: true,
diff --git a/src/app/containers/Charts/index.tsx b/src/app/containers/Charts/index.tsx
index 9c34c301d..a74ecd55f 100644
--- a/src/app/containers/Charts/index.tsx
+++ b/src/app/containers/Charts/index.tsx
@@ -23,6 +23,7 @@ import { Contracts } from './Contracts';
export function NewChart() {
const { t, i18n } = useTranslation();
const iszh = i18n.language.includes('zh');
+ dayjs.locale(i18n.language.toLowerCase() as 'zh-cn' | 'en');
const format = iszh ? 'YYYY MMMDD' : 'DD MMM YYYY';
const current = dayjs().subtract(1, 'day');
diff --git a/src/app/containers/NFTAsset/index.tsx b/src/app/containers/NFTAsset/index.tsx
index ab09e60a7..b516d3226 100644
--- a/src/app/containers/NFTAsset/index.tsx
+++ b/src/app/containers/NFTAsset/index.tsx
@@ -16,14 +16,16 @@ import { useParams, useHistory, useLocation } from 'react-router-dom';
import { NFTPreview } from 'app/components/NFTPreview';
import { AddressContainer } from 'app/components/AddressContainer';
import { Empty } from 'app/components/Empty';
-import { trackEvent } from 'utils/ga';
-import { ScanEvent } from 'utils/gaConstants';
-import { reqNFTBalances, reqNFTTokenIds } from 'utils/httpRequest';
+import {
+ reqNFTBalance,
+ reqNFTTokens,
+ reqNFT1155Tokens,
+} from 'utils/httpRequest';
import qs from 'query-string';
type NFTBalancesType = {
type: string;
- address: string;
+ contract: string;
name: any;
balance: number;
index: number;
@@ -31,101 +33,139 @@ type NFTBalancesType = {
const defaultSelectedNFT = {
type: '',
- address: '',
+ contract: '',
name: '',
balance: 0,
index: 0,
};
-export function NFTAsset() {
+export function NFTAsset({
+ contract = '',
+ type = '',
+}: {
+ contract?: string;
+ type?: string;
+}) {
const { address } = useParams<{
address?: string;
}>();
const { t, i18n } = useTranslation();
const history = useHistory();
const { pathname, search } = useLocation();
- const {
- NFTAddress: outerNFTAddress,
- skip = '0',
- limit = '12',
- ...others
- } = qs.parse(search);
+ const { NFTAddress, skip = '0', limit = '12', ...others } = qs.parse(search);
const lang = i18n.language.includes('zh') ? 'zh' : 'en';
- // const [address] = useState(routerAddress);
const [loading, setLoading] = useState(false);
const [hasSearched, setHasSearched] = useState(false);
-
- const [displayTokenIds, setDisplayTokenIds] = useState([]);
+ const [NFTs, setNFTs] = useState([]);
const [NFTBalances, setNFTBalances] = useState([]);
- const [selectedNFT, setSelectedNFT] = useState(
- defaultSelectedNFT,
- );
+ const [selectedNFT, setSelectedNFT] = useState({
+ ...defaultSelectedNFT,
+ contract,
+ type,
+ });
+ const [total, setTotal] = useState(0);
const pageSize = Number(limit);
const page = Math.floor((Number(skip) || 0) / pageSize) + 1;
- const total = selectedNFT.balance;
- const validateAddress = (address, cb) => {
+ const validateAddress = async function (address) {
if (isCurrentNetworkAddress(address)) {
if (isAccountAddress(address)) {
- cb && cb();
+ return true;
}
}
+
+ return false;
};
const handleNFTSearch = async () => {
- validateAddress(address, async () => {
- // ga
- trackEvent({
- category: ScanEvent.function.category,
- action: ScanEvent.function.action.nftChecker,
- label: address,
- });
-
- try {
- setLoading(true);
- setHasSearched(true);
-
- const data = await reqNFTBalances({
+ let NFTBalances: NFTBalancesType[] = [];
+ let selectedNFT = {
+ ...defaultSelectedNFT,
+ contract,
+ type,
+ };
+ let NFTs: any = {};
+ let total = 0;
+
+ setLoading(true);
+ setHasSearched(true);
+
+ if (contract) {
+ if (type.includes('1155')) {
+ NFTs = await reqNFT1155Tokens({
+ query: {
+ contractAddr: contract, // default NFT
+ skip: skip,
+ limit: limit,
+ },
+ });
+ } else {
+ NFTs = await reqNFTTokens({
+ query: {
+ contract: contract, // default NFT
+ skip: skip,
+ limit: limit,
+ },
+ });
+ }
+
+ // @ts-ignore
+ total = NFTs.total;
+ } else {
+ if (await validateAddress(address)) {
+ const data = await reqNFTBalance({
query: {
- ownerAddress: address,
+ owner: address,
+ limit: 10000,
},
});
- if (data && data.length > 0) {
- const NFTBalances = data.map((d, index) => ({
+ if (data.total) {
+ NFTBalances = data.list.map((d, index) => ({
...d,
index,
}));
- let selectedNFT = NFTBalances[0];
- if (outerNFTAddress) {
- selectedNFT = NFTBalances.filter(
- n => n.address === outerNFTAddress,
- )[0];
+ selectedNFT = NFTBalances[0];
+
+ if (NFTAddress) {
+ selectedNFT = NFTBalances.filter(n => n.contract === NFTAddress)[0];
}
- const nfts = await reqNFTTokenIds({
- query: {
- ownerAddress: address,
- contractAddress: selectedNFT.address, // default NFT
- skip: skip,
- limit: limit,
- },
- });
-
- setNFTBalances(NFTBalances);
- setSelectedNFT(selectedNFT);
- setDisplayTokenIds(nfts[1]);
- } else {
- setNFTBalances([]);
- setSelectedNFT(defaultSelectedNFT);
- setDisplayTokenIds([]);
+ total = selectedNFT.balance;
+
+ if (selectedNFT.type.includes('1155')) {
+ NFTs = await reqNFT1155Tokens({
+ query: {
+ contractAddr: selectedNFT.contract, // default NFT
+ userAddr: address,
+ skip: skip,
+ limit: limit,
+ },
+ });
+ } else {
+ NFTs = await reqNFTTokens({
+ query: {
+ owner: address,
+ contract: selectedNFT.contract, // default NFT
+ skip: skip,
+ limit: limit,
+ },
+ });
+ }
}
- } catch (e) {}
+ }
+ }
+
+ // @ts-ignore
+ // NFTs = NFTs.list.map(n => n.tokenId);
- setLoading(false);
- });
+ setNFTBalances(NFTBalances);
+ setSelectedNFT(selectedNFT);
+ setNFTs(NFTs.list);
+ setTotal(total);
+ setLoading(false);
};
const handlePaginationChange = (page, pageSize) => {
@@ -134,7 +174,7 @@ export function NFTAsset() {
url: pathname,
query: {
...others,
- NFTAddress: outerNFTAddress,
+ NFTAddress: NFTAddress,
skip: String((page - 1) * pageSize),
limit: String(pageSize),
},
@@ -143,7 +183,7 @@ export function NFTAsset() {
};
const handleNFTAddressChange = address => {
- setDisplayTokenIds([]);
+ setNFTs([]);
history.push(
qs.stringifyUrl({
url: pathname,
@@ -158,11 +198,9 @@ export function NFTAsset() {
};
useEffect(() => {
- if (address) {
- handleNFTSearch();
- }
+ handleNFTSearch();
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [address, outerNFTAddress, skip, limit]);
+ }, [address, NFTAddress, skip, limit, contract, type]);
return (
@@ -174,17 +212,28 @@ export function NFTAsset() {
return (
handleNFTAddressChange(n.address)}
- key={n.address}
- >{`${n.name[lang]} (${toThousands(n.balance)})`}
+ onClick={() => handleNFTAddressChange(n.contract)}
+ key={n.contract}
+ >{`${n.name} (${toThousands(n.balance)})`}
);
})}
) : null}
+
- {!loading && !NFTBalances.length ? (
+
+ {t(translations.blocks.tipCountBefore)} {toThousands(total)}{' '}
+ {lang === 'zh' ? '个 ' : ''}
+ {selectedNFT.name || ''} NFT{' '}
+
+ {t(translations.contract.address)}:{' '}
+
+
+
+
+ {!loading && !NFTs.length ? (
) : (
- <>
- {selectedNFT ? (
-
- {t(translations.blocks.tipCountBefore)} {toThousands(total)}{' '}
- {lang === 'zh' ? '个 ' : ''}
- {selectedNFT.name[lang] || ''} NFT{' '}
-
- {selectedNFT.name[lang] || ''}{' '}
- {t(translations.contract.address)}:{' '}
-
-
-
- ) : null}
-
- {displayTokenIds.map(id => (
-
-
-
- ))}
-
- >
+
+ {NFTs.map(({ tokenId, amount, owner }) => (
+
+
+
+ ))}
+
)}
{
+ const url = `/stat/nft/list1155inventory?contractAddr=${address}&tokenId=${id}`;
+
+ let columnsWidth = [2, 10, 10];
+ let columns = [
+ utils.number,
+ tokenColunms.NFTOwner,
+ tokenColunms.NFTQuantity,
+ ].map((item, i) => ({ ...item, width: columnsWidth[i] }));
+
+ return (
+
+ );
+};
diff --git a/src/app/containers/NFTDetail/TransferAndHolders.tsx b/src/app/containers/NFTDetail/TransferAndHolders.tsx
new file mode 100644
index 000000000..110bcb344
--- /dev/null
+++ b/src/app/containers/NFTDetail/TransferAndHolders.tsx
@@ -0,0 +1,29 @@
+import React from 'react';
+
+import { Holders } from './Holders';
+import { TabsTablePanel } from 'app/components/TabsTablePanel/Loadable';
+import { useTranslation } from 'react-i18next';
+import { translations } from 'locales/i18n';
+import { TransferList } from './TransferList';
+
+export const TransferAndHolders = props => {
+ const { t } = useTranslation();
+
+ const tabs: any = [
+ {
+ value: 'transfers',
+ label: t(translations.token.transfers),
+ content: ,
+ },
+ ];
+
+ if (props.type && props.type?.includes('1155')) {
+ tabs.push({
+ value: 'holders',
+ label: t(translations.token.holders),
+ content: ,
+ });
+ }
+
+ return ;
+};
diff --git a/src/app/containers/NFTDetail/TransferModal.tsx b/src/app/containers/NFTDetail/TransferModal.tsx
index d581c0bf9..d4fe6b7a8 100644
--- a/src/app/containers/NFTDetail/TransferModal.tsx
+++ b/src/app/containers/NFTDetail/TransferModal.tsx
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react';
-import { Form, Input, Button, Modal } from '@cfxjs/antd';
+import { Form, Input, Button, Modal, InputNumber } from '@cfxjs/antd';
import { useTranslation } from 'react-i18next';
import { translations } from 'locales/i18n';
import styled from 'styled-components';
@@ -19,7 +19,6 @@ import { TxnStatusModal } from 'app/components/ConnectWallet/TxnStatusModal';
import SDK from 'js-conflux-sdk/dist/js-conflux-sdk.umd.min.js';
export const TransferModal = ({
- owner = '',
id = '',
contractAddress = '',
contractType = '',
@@ -39,6 +38,7 @@ export const TransferModal = ({
status: '',
errorMessage: '',
});
+ const [NFT1155Quantity, setNFT1155Quantity] = useState(0);
const isNFT721 = contractType?.includes('721');
const contract = useMemo(() => {
@@ -56,23 +56,22 @@ export const TransferModal = ({
useEffect(() => {
async function fn() {
- if (owner && id) {
+ if (id) {
if (isNFT721) {
let isOwner = false;
if ((await contract.ownerOf(id)) === account) {
isOwner = true;
- } else if ((await contract.getApproved(id)) === account) {
- isOwner = true;
}
setIsOwner(isOwner);
} else {
let isOwner = false;
- if (owner === account) {
- isOwner = true;
- } else if (await contract.isApprovedForAll(owner, account)) {
+ const quantity = Number(await contract.balanceOf(account, id));
+ setNFT1155Quantity(quantity);
+
+ if (quantity) {
isOwner = true;
}
@@ -84,7 +83,7 @@ export const TransferModal = ({
fn().catch(e => {
console.log('error: ', e);
});
- }, [contract, contractAddress, contractType, account, id, isNFT721, owner]);
+ }, [contract, contractAddress, contractType, account, id, isNFT721]);
const validator = useCallback(() => {
return {
@@ -113,6 +112,24 @@ export const TransferModal = ({
};
}, [t]);
+ const amountValidator = useCallback(() => {
+ return {
+ validator(_, value) {
+ if (value > 0 && value <= NFT1155Quantity) {
+ return Promise.resolve();
+ } else {
+ return Promise.reject(
+ new Error(
+ t(translations.nftDetail.error.invalidAmount, {
+ amount: NFT1155Quantity,
+ }),
+ ),
+ );
+ }
+ },
+ };
+ }, [t, NFT1155Quantity]);
+
const showTransferModal = () => {
setIsModalVisible(true);
};
@@ -120,37 +137,40 @@ export const TransferModal = ({
const handleOk = () => {
form
.validateFields()
- .then(({ fromAddress, toAddress, tokenId }) => {
+ .then(async function ({ fromAddress, toAddress, tokenId, amount }) {
setSubmitLoading(true);
setTxnStatusModal({
...txnStatusModal,
show: true,
});
+ let hash = '';
+
if (isNFT721) {
- return contract
+ hash = await contract
.safeTransferFrom(fromAddress, toAddress, tokenId)
.sendTransaction({ from: account });
} else {
- return contract
- .safeTransferFrom(fromAddress, toAddress, tokenId, 1, '0x')
+ hash = await contract
+ .safeTransferFrom(fromAddress, toAddress, tokenId, amount, '0x')
.sendTransaction({ from: account });
}
- })
- .then(hash => {
+
setTxnStatusModal({
...txnStatusModal,
show: true,
hash,
});
+
addRecord({
hash,
info: JSON.stringify({
- code: TXN_ACTION.tranferNFT,
+ code: isNFT721 ? TXN_ACTION.tranferNFT : TXN_ACTION.tranferNFT1155,
description: '',
hash,
id: id,
type: contractType,
+ amount,
}),
});
})
@@ -209,57 +229,80 @@ export const TransferModal = ({
onOk={handleOk}
onCancel={handleCancel}
>
- {id && owner && (
+ {id && (
-
+
-
+
+ {!isNFT721 && (
+
+
+
+ )}
)}
diff --git a/src/app/containers/NFTDetail/index.tsx b/src/app/containers/NFTDetail/index.tsx
index 96d0711c9..e63b8316f 100644
--- a/src/app/containers/NFTDetail/index.tsx
+++ b/src/app/containers/NFTDetail/index.tsx
@@ -22,7 +22,7 @@ import 'ace-builds/src-noconflict/theme-tomorrow';
import { formatTimeStamp } from 'utils';
-import { TransferList } from './TransferList';
+import { TransferAndHolders } from './TransferAndHolders';
import { TransferModal } from './TransferModal';
import lodash from 'lodash';
@@ -98,7 +98,6 @@ export function NFTDetail(props) {
{bp !== 's' && (
-
-
- {data.owner ? (
- <>
-
- {data.owner}
- {' '}
-
- >
- ) : (
- '--'
- )}
-
-
{tokenType ? tokenType : '--'}
@@ -231,12 +216,13 @@ export function NFTDetail(props) {
-
+ key={data.type}
+ />
);
diff --git a/src/app/containers/TokenDetail/NFTs.tsx b/src/app/containers/TokenDetail/NFTs.tsx
deleted file mode 100644
index 38e51fd24..000000000
--- a/src/app/containers/TokenDetail/NFTs.tsx
+++ /dev/null
@@ -1,260 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import { Card } from '../../components/Card';
-import { Spin, Row, Col, Pagination, Divider } from '@cfxjs/antd';
-import { toThousands } from '../../../utils';
-import { Empty } from '../../components/Empty';
-import { translations } from '../../../locales/i18n';
-import { NFTPreview } from '../../components/NFTPreview';
-import styled from 'styled-components/macro';
-import { media } from '../../../styles/media';
-import { useTranslation } from 'react-i18next';
-import { ScanEvent } from '../../../utils/gaConstants';
-import { Select } from '../../components/Select';
-import { trackEvent } from '../../../utils/ga';
-import qs from 'query-string';
-import { useHistory, useLocation } from 'react-router-dom';
-import { reqNFTTokenIdsInTokenPage } from '../../../utils/httpRequest';
-
-export const NFTs = ({ address }) => {
- const { t } = useTranslation();
- const history = useHistory();
- const { pathname, search } = useLocation();
- const { skip = '0', limit = '12' } = qs.parse(search);
- const [loading, setLoading] = useState(false);
- const [total, setTotal] = useState(0);
- const [displayTokenIds, setDisplayTokenIds] = useState([]);
- const [option, setOption] = useState('recentlyActive');
- const options = [
- {
- value: 'recentlyActive',
- name: t(translations.token.recentlyActive),
- },
- {
- value: 'newest',
- name: t(translations.token.newest),
- },
- {
- value: 'oldest',
- name: t(translations.token.oldest),
- },
- ];
-
- const pageSize = Number(limit);
- const page = Math.floor((Number(skip) || 0) / pageSize) + 1;
-
- const handleChangeOption = option => {
- if (option === 'recentlyActive') {
- trackEvent({
- category: ScanEvent.token.category,
- action: ScanEvent.token.action.recentlyActive,
- label: 'recentlyActive',
- });
- setOption('recentlyActive');
- } else if (option === 'newest') {
- trackEvent({
- category: ScanEvent.token.category,
- action: ScanEvent.token.action.recentlyActive,
- label: 'newest',
- });
- setOption('newest');
- } else {
- trackEvent({
- category: ScanEvent.token.category,
- action: ScanEvent.token.action.recentlyActive,
- label: 'oldest',
- });
- setOption('oldest');
- }
- };
- const handlePaginationChange = (page, pageSize) => {
- history.push(
- qs.stringifyUrl({
- url: pathname,
- query: {
- tab: 'NFT',
- skip: String((page - 1) * pageSize),
- limit: String(pageSize),
- },
- }),
- );
- };
- const getTokenIds = async () => {
- setLoading(true);
- setDisplayTokenIds([]);
- const data = await reqNFTTokenIdsInTokenPage({
- query: {
- contractAddress: address,
- limit,
- skip,
- },
- });
- const tokenIds = data.rows.reduce((oldArray, currentValue) => {
- oldArray.push(currentValue.tokenId);
- return oldArray;
- }, []);
- setDisplayTokenIds(tokenIds);
- setTotal(data.count);
- setLoading(false);
- };
-
- useEffect(() => {
- getTokenIds();
- }, [address, skip, limit]); // eslint-disable-line react-hooks/exhaustive-deps
-
- return (
-
-
-
-
- {!loading && (!displayTokenIds || displayTokenIds.length === 0) ? (
-
-
-
- ) : (
- <>
- {total > 0 ? (
-
-
- {t(translations.token.total)}{' '}
- {toThousands(total)}
- {t(translations.token.tokens)}
-
-
-
- {t(translations.token.sortBy)}
-
-
-
-
- ) : null}
-
-
- {displayTokenIds.map((id, i) => (
-
-
-
- ))}
-
- >
- )}
-
-
-
-
-
-
- );
-};
-
-const StyledResultWrapper = styled.div`
- margin-top: 1.7143rem;
-
- .convert-address-description {
- flex-direction: row;
-
- .left {
- width: 35.7143rem;
- }
- }
-`;
-const NFTWrapper = styled.div`
- width: 100%;
- padding: 16px 0 40px;
- min-height: 500px;
-
- .nodata {
- margin-top: 100px;
- color: #74798c;
- }
-
- .total {
- color: #002257;
- margin-bottom: 14px;
-
- > span {
- float: right;
-
- ${media.m} {
- display: block;
- float: none;
- }
- }
- }
-
- .ant-divider-horizontal {
- margin: 12px 0 24px 0;
- }
-
- .ant-pagination {
- margin-top: 10px;
- text-align: right;
-
- li {
- margin-bottom: 0;
-
- &:before {
- display: none;
- }
-
- . anticon {
- vertical-align: 1px;
- }
- }
- }
-
- .ant-col {
- padding-bottom: 20px;
- }
-`;
-const HeaderWrapper = styled.div`
- display: flex;
- justify-content: space-between;
- line-height: 36px;
- color: #737682 !important;
-`;
-const TotalWrapper = styled.div``;
-const NumberWrapper = styled.span`
- color: #1e3de4;
-`;
-const SelectWrapper = styled.span`
- //display: flex;
- // todo sort框以后再加进来
- display: none;
- align-items: center;
- width: 198px;
-
- div {
- font-size: 14px !important;
- color: #737682 !important;
- }
-`;
-const SortByWrapper = styled.span`
- line-height: 36px;
-`;
diff --git a/src/app/containers/TokenDetail/Transfers.tsx b/src/app/containers/TokenDetail/Transfers.tsx
index 90562eec8..fd5c5509c 100644
--- a/src/app/containers/TokenDetail/Transfers.tsx
+++ b/src/app/containers/TokenDetail/Transfers.tsx
@@ -14,8 +14,8 @@ import { Token } from '../Charts/Loadable';
import { Transfers as TokenTransfers } from 'app/containers/Tokens/Loadable';
import { Holders } from './Holders';
-import { NFTs } from './NFTs';
import lodash from 'lodash';
+import { NFTAsset } from 'app/containers/NFTAsset';
interface TransferProps {
tokenName: string;
@@ -148,7 +148,7 @@ export function Transfers({ tokenData }: { tokenData: TransferProps }) {
value: 'NFT',
action: 'tokenNFT',
label: t(translations.token.NFT),
- content: ,
+ content: ,
});
}
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index 81f64ebcb..2cf05efe8 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -19,12 +19,16 @@
"transfer": "Transfer",
"from": "From",
"to": "To",
+ "amount": "Amount",
+ "amountTip": "Owned amount is {{amount}}",
"error": {
"fromAddress": "Please input from address",
"toAddress": "Please input to address",
+ "amount": "Please input amount",
"id": "Please input token ID",
"invalidAddress": "Please input valid base32 address",
- "invalidNetwork": "Please input {{network}} address"
+ "invalidNetwork": "Please input {{network}} address",
+ "invalidAmount": "Please input 1 - {{amount}}"
}
},
"fccfx": {
@@ -1479,7 +1483,8 @@
"111": "{{value}} FC sign up for subsidy",
"112": "Announce Agreement",
"113": "Withdraw of principal {{value}} CFX",
- "114": "Transfer {{type}} NFT (Token ID: {{id}})"
+ "114": "Transfer {{type}} NFT (Token ID: {{id}})",
+ "115": "Transfer {{type}} NFT (Token ID: {{id}}, Amount: {{amount}})"
}
},
"tip": "Please connect to Fluent Wallet first!"
@@ -1646,7 +1651,9 @@
"incorrectAddressType": "Invalid address type, only support for account address",
"tokenId": "TokenID",
"plzSearch": "Plz Search",
- "plzSearchDesc": "Please enter an address above to load the NFTs it owned"
+ "plzSearchDesc": "Please enter an address above to load the NFTs it owned",
+ "owner": "Owner",
+ "amount": "NFT Quantity"
},
"pos": {
"common": {
diff --git a/src/locales/zh_cn/translation.json b/src/locales/zh_cn/translation.json
index 3d4df2865..cda55f57d 100644
--- a/src/locales/zh_cn/translation.json
+++ b/src/locales/zh_cn/translation.json
@@ -19,12 +19,16 @@
"transfer": "转发",
"from": "发送方",
"to": "接收方",
+ "amount": "数量",
+ "amountTip": "持有 {{amount}} 个",
"error": {
"fromAddress": "请输入发送方地址",
"toAddress": "请输入接收方地址",
+ "amount": "请输入数量",
"id": "请输入代币 ID",
"invalidAddress": "请输入合法地址",
- "invalidNetwork": "请输入{{network}}地址"
+ "invalidNetwork": "请输入{{network}}地址",
+ "invalidAmount": "请输入 1 - {{amount}} 个"
}
},
"fccfx": {
@@ -1477,7 +1481,8 @@
"111": "{{value}} 个 FC 签约补贴计划",
"112": "通知协议",
"113": "提取本金 {{value}} CFX",
- "114": "转移 {{type}} NFT(代币 ID:{{id}})"
+ "114": "转移 {{type}} NFT(代币 ID:{{id}})",
+ "115": "转移 {{type}} NFT(代币 ID:{{id}},数量:{{amount}})"
}
},
"tip": "请先连接 Fluent Wallet 钱包!"
@@ -1644,7 +1649,9 @@
"incorrectAddressType": "地址类型错误,仅支持查询个人账户地址",
"tokenId": "代币 ID",
"plzSearch": "请搜索",
- "plzSearchDesc": "请在上方输入地址以查看其拥有的 NFT"
+ "plzSearchDesc": "请在上方输入地址以查看其拥有的 NFT",
+ "owner": "所有人",
+ "amount": "NFT 数量"
},
"pos": {
"common": {
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 4696f4745..35bceed66 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -157,6 +157,7 @@ export enum TXN_ACTION {
fccfxSignToSyncInterest = 112,
fccfxWithdrawCFX = 113,
tranferNFT = 114,
+ tranferNFT1155 = 115,
}
export const CURRENCY_SYMBOLS = {
@@ -221,6 +222,7 @@ export const OPEN_API_HOST = IS_TESTNET
: 'api.confluxscan.net';
export const OPEN_API_URLS = Object.entries({
+ // charts
mining: '/statistics/mining',
supply: '/statistics/supply',
tps: '/statistics/tps',
@@ -231,6 +233,10 @@ export const OPEN_API_URLS = Object.entries({
accountGrowth: '/statistics/account/growth',
activeAccounts: '/statistics/account/active',
contracts: '/statistics/contract',
+
+ // NFT
+ NFTTokens: '/nft/tokens',
+ NFTBalance: '/nft/balances',
})
.map(item => ({
[item[0]]: `https://${OPEN_API_HOST}${item[1]}`,
diff --git a/src/utils/contract/ERC1155.json b/src/utils/contract/ERC1155.json
index f24fd66ee..d3bb924aa 100644
--- a/src/utils/contract/ERC1155.json
+++ b/src/utils/contract/ERC1155.json
@@ -1,5 +1,29 @@
{
"abi": [
+ {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "_owner",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "_id",
+ "type": "uint256"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
{
"inputs": [
{
diff --git a/src/utils/hooks/useHighcharts.ts b/src/utils/hooks/useHighcharts.ts
index a9183c190..bc0dcf87c 100644
--- a/src/utils/hooks/useHighcharts.ts
+++ b/src/utils/hooks/useHighcharts.ts
@@ -66,6 +66,24 @@ const localeData = {
},
};
+const tooltipData = {
+ en: {
+ xDateFormat: '%a %e, %b %Y (UTC)',
+ },
+ zh: {
+ xDateFormat: '%a, %b %e, %Y (UTC)',
+ },
+};
+
+const xAxisData = {
+ en: {
+ labels: { format: `{value:%e '%b}` },
+ },
+ zh: {
+ labels: { format: `{value:%b '%e}` },
+ },
+};
+
export const useHighcharts = (chart?) => {
const { i18n } = useTranslation();
const lang = i18n.language.includes('zh') ? 'zh' : 'en';
@@ -73,10 +91,14 @@ export const useHighcharts = (chart?) => {
useEffect(() => {
Highstock.setOptions({
lang: localeData[lang],
+ tooltip: tooltipData[lang],
+ xAxis: xAxisData[lang],
});
Highcharts.setOptions({
lang: localeData[lang],
+ tooltip: tooltipData[lang],
+ xAxis: xAxisData[lang],
});
const c = chart.current?.chart;
diff --git a/src/utils/httpRequest.ts b/src/utils/httpRequest.ts
index 2d799cb26..64c32796f 100644
--- a/src/utils/httpRequest.ts
+++ b/src/utils/httpRequest.ts
@@ -1,5 +1,6 @@
import qs from 'query-string';
import fetch from './request';
+import { OPEN_API_URLS } from './constants';
export const v1Prefix = '/v1';
export const statPrefix = '/stat';
@@ -221,25 +222,6 @@ export const reqTransactions = (extra?: object) => {
});
};
-export const reqNFTBalances = (extra?: object) => {
- return sendRequest({
- url: `/stat/nft/checker/balance`,
- ...extra,
- });
-};
-
-export const reqNFTTokenIds = (extra?: object) => {
- return sendRequest({
- url: `/stat/nft/checker/token`,
- ...extra,
- });
-};
-export const reqNFTTokenIdsInTokenPage = (extra?: object) => {
- return sendRequest({
- url: `/stat/nft/active-token-ids`,
- ...extra,
- });
-};
export const reqNFTInfo = (extra?: object) => {
// ?contractAddress=cfx:acb3fcbj8jantg52jbg66pc21jgj2ud02pj1v4hkwn&tokenId=424873
return sendRequest({
@@ -304,6 +286,15 @@ export const reqPoSIncomingHistory = (extra?: object) => {
});
};
+export const reqNFT1155Tokens = (extra?: object) => {
+ return sendRequest({
+ url: `/stat/nft/list1155inventory`,
+ ...extra,
+ });
+};
+
+/** open api, start */
+
/** charts, start */
export const reqChartData = ({ url, query }) => {
@@ -314,3 +305,19 @@ export const reqChartData = ({ url, query }) => {
};
/** charts, end */
+
+export const reqNFTTokens = (extra?: object) => {
+ return sendRequest({
+ url: OPEN_API_URLS.NFTTokens,
+ ...extra,
+ });
+};
+
+export const reqNFTBalance = (extra?: object) => {
+ return sendRequest({
+ url: OPEN_API_URLS.NFTBalance,
+ ...extra,
+ });
+};
+
+/** open api, end */
diff --git a/src/utils/tableColumns/token.tsx b/src/utils/tableColumns/token.tsx
index b55ec4c74..de90a1fae 100644
--- a/src/utils/tableColumns/token.tsx
+++ b/src/utils/tableColumns/token.tsx
@@ -853,6 +853,45 @@ export const projectInfo = {
},
};
+export const NFTOwner = {
+ width: 1,
+ title: (
+
+ {t => t(translations.general.table.token.accountAddress)}
+
+ ),
+ dataIndex: 'owner',
+ key: 'owner',
+ render: (value, row) => (
+
+
+
+ ),
+};
+
+export const NFTQuantity = {
+ width: 1,
+ title: (
+
+
+ {t => t(translations.general.table.token.quantity)}
+
+
+ ),
+ dataIndex: 'amount',
+ key: 'amount',
+ render: value => {value},
+};
+
export const StyledIconWrapper = styled.div`
display: flex;
align-items: center;