/*
 * last modified---
 * 	08-14-26 abandon use of getPastEvents() due to RPC throttling; use config
 * 	06-06-26 add use of DOM ids for browser automation support
 * 	03-21-25 use chain.link price feeds where available
 * 	01-29-24 add link to Explainer page
 * 	12-05-23 show token symbol in <TotalItem>
 * 	11-21-23 use BigNumber for values in <TotalItem>, market value computation
 * 	10-20-23 refactor to store eNFTs[] in local state and establish availability
 * 	10-17-23 pass eNFTList as array
 * 	10-03-23 show sig valid icons in Badges
 * 	09-11-23 use Contract.getPastEvents(), check for DepositETH events
 * 	08-07-23 use LoadingButton
 * 	08-02-23 rework to key off DepositERC20 events as well as prefab token confs
 * 	07-18-23 pull chainConn from useEth() state
 * 	06-02-23 add sendWalletReqToMVO(), moved from Enshroud.jsx
 *
 * purpose---
 * 	display wallet eNFT and on-deposit asset contents
 */

import React, { useState } from 'react';
import Container from 'react-bootstrap/Container';
import Table from 'react-bootstrap/Table';
import Button from 'react-bootstrap/Button';
import Image from 'react-bootstrap/Image';
import Card from 'react-bootstrap/Card';
import Accordion from 'react-bootstrap/Accordion';
import { useAccordionButton } from 'react-bootstrap/AccordionButton';
import Badge from 'react-bootstrap/Badge';
import useEth from './EthContext/useEth';
import MVOComm from './MVOComm.js';
import LoadingButton from './LoadingButton.jsx';
import NoticeWrongNetwork from './Notices.jsx';
import DataToast from './DataToast.jsx';
import ChainLinkPriceFeed from './priceFeeds.js';
const BigNumber = require("bignumber.js");


/* render a total as a table row
 * @param props.asset the token address
 * @param props.total the amount of token deposited (ethers)
 * @param props.avail the portion of amount which is confirmed available (eths)
 * @param props.id the key Id (array index)
 * @param props.marketPrices the market prices of tokens, if any
 */
function TotalItem(props) {
    const { state: { web3 } } = useEth();

    // symbol for the indicated asset (use address until fetched)
    const [assetSymbol, setAssetSymbol] = useState(props.asset);

	// get symbol of an erc20-compatible contract
	const getTokenSymbol = async (tokenAddr) => {
		var symbol = '';
		const callData = web3.eth.abi.encodeFunctionCall({
			name: 'symbol',
			type: 'function',
			constant: true,
			inputs: []
		}, []);
		await web3.eth.call({
			to: tokenAddr,	// erc20/erc777/erc4626 contract address
			data: callData	// function call encoding
		})
			.then(sym => {
				symbol = web3.eth.abi.decodeParameter('string', sym);
			}).catch(err => {
				console.error("error fetching symbol at " + tokenAddr + ": "
							+ err);
			});
		return symbol;
	};

	// method to call getTokenSymbol()
	async function getSymbol() {
		const tokenAddr = props.asset;
		// fetch actual symbol
		const symbol = await getTokenSymbol(tokenAddr);
		if (symbol !== '' && symbol !== assetSymbol) {
			setAssetSymbol(symbol);
		}
	}
	getSymbol();

	const ethsTot = new BigNumber(props.total);
	const ethsAvail = new BigNumber(props.avail);
	var amtCol = props.total;
	var amtSuffix = '';
	if (ethsTot.gt(ethsAvail)) {
		amtSuffix = " (" + ethsAvail.toFixed() + ")";
	}

	// show available total at market price if we have one available
	var marketValue = "N/A";
	const mPrice = props.marketPrices.find(elt => elt.asset === props.asset);
	if (mPrice !== undefined) {
		const value = ethsAvail.times(mPrice.price).decimalPlaces(2);
		marketValue = value.toString();
	}

	// render item total
	const amtId = "amts-" + props.asset;
	const symId = "sym-" + props.asset;
	return (
		<tr key={`tot${props.id}`} align="center" valign="middle">
			<td title={props.asset} id={symId}>{assetSymbol}</td>
			<td id={amtId}>{amtCol}<i>{amtSuffix}</i></td>
			<td>{marketValue}</td>
		</tr>
	);
}

// provide button to toggle the list of eNFTs constituting a total
function NftListToggle({children, eventKey}) {
	const decoratedOnClick = useAccordionButton(eventKey, () =>
		"",
	);

	const bId = "listToggle-" + eventKey;
	return (
		<Button onClick={decoratedOnClick} variant="info" size="lg" id={bId}
			title="Toggle a detail list of your eNFTs of this asset type"
		>
			{children}
		</Button>
	);
}

/* render the details of eNFTs for an asset
 * @param props.asset the token contract address
 * @param props.id key value (for React)
 * @param props.enftData the list of eNFTs of this token type
 */
function AssetENFTdetails(props) {
    const { state: { web3, chainConn } } = useEth();

	return (
		<tr align="left" valign="middle">
			<td colSpan="3">
				<Accordion flush>
					<Accordion.Item eventKey={props.id}>
						<NftListToggle eventKey={props.id}
							key={`toggle${props.id}`} >
							Show&nbsp;
							<a href={`${chainConn.chainConfig.scanURL}/address/${props.asset}`} title="Examine asset contract" target="_blank" rel="noreferrer noopener">
							{props.asset}</a> eNFTs&nbsp;
							<Badge bg="success">
								{props.enftData.length}
							</Badge>&nbsp;&nbsp;
							<Image src="images/zoom-in.svg"
								height={40} width={40} fluid rounded
							/>
						</NftListToggle>
						<Accordion.Body>
							<div className="accordion-body">
							<Table striped bordered hover responsive
								variant="dark">
								<caption className="caption-top">
									Details of your eNFTs representing token
									asset <i>{props.asset}</i>:
								</caption>
								<thead>
									<tr align="center" key={`hdr${props.id}`}>
										<th scope="col"
											title="Shown in ethers units for readability">
											Amount (1e-18)
										</th>
										<th scope="col">eNFT ID</th>
										<th scope="col"
											title="Number of times this value has been circulated since it was deposited on-chain">
											Generation
										</th>
										<th scope="col"
											title="Results of checking the MVO signature on this eNFT">
											Sig Verified
										</th>
										<th scope="col"
											title="Decrypted eNFT metadata, AES-256 decryption key (Base64), and status">
											Metadata + AES Key
										</th>
									</tr>
								</thead>
								<tbody>
									{props.enftData.map((enft) =>
										<tr align="center" valign="middle"
											key={enft.id}>
											<td>
											  {web3.utils.fromWei(enft.amount)}
											  &nbsp;&nbsp;
											  {enft.avail ?
											  	<Badge pill bg="success"
													title={"Available " + enft.unavailReason}>
													+</Badge>
												: <Badge pill bg="warning"
													title={"Unavailable: " + enft.unavailReason}>
													-</Badge>
											  }
											</td>
											<td className="text-break">
												{enft.id}
											</td>
											<td>{enft.generation}</td>
											<td>
											{enft.validateSig(chainConn, web3)
												? <Badge pill bg="success"
												title="MVO signature validated">
													<Image
														src="images/check2.svg"
														fluid rounded
														className="p-2"
														height={40} width={40}/>
												  </Badge>
												: <Badge pill bg="danger"
												title="MVO signature invalid">
													<Image
														src="images/x-lg.svg"
														fluid rounded
														className="p-2"
														height={40} width={40}/>
												  </Badge>
											}
											</td>
											<td className="text-break">
												<DataToast
													variant="info"
													title="Decrypted eNFT info"
													buttonText="eNFT metadata"
													buttonIcon="images/key.svg"
													buttonTitle="Display decrypted metadata, AES key (in Base64URL format), and status"
													index={enft.id}
													data={JSON.stringify(enft)}
												/>
											</td>
										</tr>
									)}
								</tbody>
							</Table>
							</div>
						</Accordion.Body>
					</Accordion.Item>
				</Accordion>
			</td>
		</tr>
	);
}

/* render an entire table of eNFTs
 * @param props.row a record of this type:
 *  {id: key counter, asset: address, total: <eNFT sum>, avail: <sum avail>,
 *  eNFTs: [Enft], value: <market value of total>}
 * @param props.marketPrices the array of market prices from available feeds
 */
function ENFTTable(props) {
	// render body of table
	return (
		<tbody>
			{props.details.map((row) =>
				<React.Fragment key={"frag_" + row.id}>
					<TotalItem
						asset={row.asset}
						total={row.total}
						avail={row.avail}
						marketPrices={props.marketPrices}
						id={row.id}
					/>
					<AssetENFTdetails
						asset={row.asset}
						enftData={row.eNFTs}
						id={row.id}
					/>
				</React.Fragment>
			)}
		</tbody>
	);
}

/* render the whole table of asset balances on deposit
 */
function DepositTable(props) {
	    // enable use of our contracts, accounts, and Web3 connection
    const { state: { contracts, accounts, web3, chainConn } } = useEth();
	var enshProtoContract = contracts["EnshroudProtocol"];
	const userAcct = web3.utils.toChecksumAddress(accounts[0]);
	var startBlock = chainConn.chainConfig.tokenGenesis;
	if (startBlock === undefined) {
		console.error("No tokenGenesis found for EnshroudToken on chain Id "
						+ chainConn.chainConfig.chainId);
		startBlock = "earliest";
	}

    // all assets ever deposited by the particular user account (ChainAssetS)
    const [userAssets, setUserAssets] = useState([]);

	// get balance on deposit in the EnshroudProtocol contract
	const checkDepositBalance = async (tokenAddr) => {
		const bal = await enshProtoContract.methods.assetBalances(
								userAcct, tokenAddr).call({ from: userAcct });
		return bal;
	};

	// obtain the set of all assets ever deposited on this chain by this user
	const buildAssetConfig = async (resolve, reject) => {
		// start with preset asset configs
		var protocolAssets = [].concat(chainConn.chainConfig.assetList);
		var depositAssets = [];

		/* Next, fetch the user's current deposited balance in every asset
		 * they've ever deposited to the contract.  Also fetch the market
		 * price, if we have a price feed contract available.
		 */
		const clFeed = new ChainLinkPriceFeed();
		for await (const assetConfig of protocolAssets) {
			const userBal
				= await checkDepositBalance(assetConfig.contractAddress);
			if (userBal > 0) {
				assetConfig.balanceOf = userBal;
				// get USD value of this asset if we can
				if (assetConfig.priceFeed !== undefined
					&& assetConfig.priceFeed !== '')
				{
					const usdPrice
						= await clFeed.getPrice(web3, assetConfig.priceFeed);
					if (usdPrice !== undefined) {
						const usdVal
							= new BigNumber(assetConfig.balanceOf.toString())
											.times(usdPrice).decimalPlaces(4);
						assetConfig.value = usdVal.toString();
					}
				}
				else {
					assetConfig.value = "N/A";
				}
				depositAssets.push(assetConfig);
			}
		}

		// now tell React we have all the data
		setUserAssets(userAssets => ([...depositAssets]));
		resolve(true);
	};

	// render deposit balance table contents
	const bodyContents = (
		<>
		<tr align="left" valign="middle" key="popButton">
			<td colSpan="4">
				<LoadingButton
					variant="primary"
					buttonTitle="Populate table with your deposited balances"
					netMethod={(resolve, reject) => buildAssetConfig(resolve, reject)}
					buttonText="Refresh Your Deposits"
					buttonIcon="images/download.svg"
				/>
			</td>
		</tr>
		{userAssets.length > 0 && userAssets.map((assetConfig) =>
			<React.Fragment key={assetConfig.symbol}>
				<tr align="center" valign="middle">
					<td title={assetConfig.contractAddress}>
						{assetConfig.symbol}
					</td>
					<td>
						{web3.utils.fromWei(assetConfig.balanceOf.toString())}
					</td>
					<td title="Market value in USD of deposited balance">
						{assetConfig.value}
					</td>
				</tr>
			</React.Fragment>
		)}
		</>
	);
	return (
		<tbody>
			{bodyContents}
		</tbody>
	);
}

/* render the Home page, including deposited assets and eNFTs
 * @param props.parseReplyMethod method to process replies from MVOs
 * @param props.onSelect method to shift to other pages
 * @param props.eNFTList list of eNFTs belonging to user
 */
function HomePage(props) {
	// NB: on first render, all these will be null before chainConn gets set
	const { state: { accounts, contracts, chainConn, web3 } } = useEth();

	// state storage of passed eNFTs, downloaded and decrypted and passed down
	const [eNFTs, setENFTs] = useState([]);

	/* state storage of market prices for assets represented by eNFTs
	 * array elements of the form: {asset: <contractAddr>, price: <USD/unit>}
	 */
	const [marketPrices, setMarketPrices] = useState([]);

	// audit that passed eNFTs (if any) are known (NB: all are decrypted)
	const newEnfts = [];
	props.eNFTList.forEach(enft => {
		const existEnft = eNFTs.find(elt => elt.id === enft.id);
		if (existEnft === undefined) {
			// add
			newEnfts.push(enft);
		}
	});
	if (newEnfts.length > 0) {
		if (eNFTs.length === 0) {
			setENFTs([...newEnfts]);
		} else {
			setENFTs([...eNFTs, ...newEnfts]);
		}

		// fetch and update availability of these EnftS
		if (contracts !== null) {
			checkEnftAvailability(newEnfts);
		}
	}

	/* method to check the availability of passed eNFTs
	 * @param enftList the list of eNFTs to check
	 */
	async function checkEnftAvailability(enftList) {
		const epContract = contracts["EnshroudProtocol"];
		const redoEnfts = [];
		for await (const enft of enftList) {
			/* Determine whether the eNFT is "available," meaning aged for the
			 * required number of blocks since minting, plus not greylisted.
			 * This async method sets enft.avail, and if set to false, also
			 * sets enft.unavailReason showing why.
			 */
			await enft.isAvailable(web3, epContract);

			// update by merging state
			redoEnfts.push(enft);
		}

		// mass delete and re-add any that changed (queued w/ updater fn.)
		if (redoEnfts.length > 0) {
			// NB: this will trigger two re-renders, one after each operation
			setENFTs(eNFTs => (eNFTs.filter(
						elt => !redoEnfts.some(enft => enft.id === elt.id))));
			setENFTs(eNFTs => ([...eNFTs, ...redoEnfts]));
		}
	}

	/* method to obtain the market price of assets for which we have feeds
	 * @param asset the contract address (erc20; can't be native token w/ eNFT)
	 */
	async function getMarketPrice(asset) {
		// establish market value if we have a known price feed
		var mktPrice = undefined;
		var mktPriceFeed = '';
		const chAsset = chainConn.chainConfig.assetList.find(
										elt => elt.contractAddress === asset);
		if (chAsset !== undefined) {
			mktPriceFeed = chAsset.priceFeed;
		}
		if (mktPriceFeed !== undefined && mktPriceFeed !== '') {
			// get the market price for this asset
			const clFeed = new ChainLinkPriceFeed();
			const usdPrice = await clFeed.getPrice(web3, mktPriceFeed);
			if (usdPrice !== undefined) {
				mktPrice = usdPrice.decimalPlaces(4);
			}
		}
		if (mktPrice !== undefined) {
			const priceEntry = { asset: asset, price: mktPrice};
			const existingEntry = marketPrices.find(elt => elt.asset === asset);
			if (existingEntry !== undefined) {
				setMarketPrices(marketPrices => (marketPrices.map(elt => {
									if (elt.asset === priceEntry.asset) {
										return priceEntry;
									} else {
										return elt;
									}
								})));
			} else {
				setMarketPrices(
							marketPrices => ([...marketPrices, priceEntry]));
			}
		}
	}

	// based on passed-in EnftS, build a list of assets, totals, prices, eNFTs
	const assetTotals = new Map();		// contract -> total in wei (ledger)
	const assetAvails = new Map();		// contract -> total in wei (confirmed)
	const assetENFTs = new Map();		// contract -> array of Enft
	eNFTs.forEach(enft => {
		let asset = enft.asset;
		if (assetTotals.has(asset)) {
			// increase the total
			let existAmt = assetTotals.get(asset);
			existAmt.iadd(web3.utils.toBN(enft.amount));
			assetTotals.set(asset, existAmt);

			// add Enft to array
			assetENFTs.get(asset).push(enft);
		}
		else {
			// initialize the total
			assetTotals.set(asset, web3.utils.toBN(enft.amount));

			// init array with this Enft
			const nftArray = [];
			nftArray.push(enft);
			assetENFTs.set(asset, nftArray);
		}

		// add this Enft to available balance if it's marked available
		if (enft.avail) {
			if (assetAvails.has(asset)) {
				// increase
				let existAvail = assetAvails.get(asset);
				existAvail = existAvail.add(web3.utils.toBN(enft.amount));
				assetAvails.set(asset, existAvail);
			} else {
				// init total
				assetAvails.set(asset, web3.utils.toBN(enft.amount));
			}
		}
	});

	// construct data to pass to ENFTTable as props
	let assetIdx = 1;
	const eNFTTableProps = [];
	for (const asst of assetTotals.keys()) {
		const amount = web3.utils.fromWei(assetTotals.get(asst));
		const avBucket = assetAvails.get(asst);
		var avail = "0";
		if (avBucket !== undefined) {
			avail = web3.utils.fromWei(avBucket);
		}

		const nftArray = assetENFTs.get(asst);
		eNFTTableProps.push({id: assetIdx++,
							asset: asst,
							total: amount,
							avail: avail,
							eNFTs: nftArray});

		// look for prices if any are available
		getMarketPrice(asst);
	}

	// method to send a signed wallet download request to an MVO
	async function sendWalletReqToMVO(resolve, reject) {
		// examine passed MVO configuration to ensure it's been downloaded
		const mvoConfig = chainConn.MVOConf;
		const chId = chainConn.chainConfig.chainId;
		if (mvoConfig.availableMVOs.length === 0) {
			let inputErr = new Error("No MVOs listed for chainId " + chId
					+ "; is " + chainConn.chainConfig.chain + " connected?");
			alert(inputErr.message);
			reject(inputErr);
			return false;
		}

		// obtain secure communicator to randomly selected MVO for this chain
		const mvoComm = mvoConfig.getMVOCommunicator('wallet', true);
		if (!(mvoComm instanceof MVOComm)) {
			let mvoErr = new Error("Could not select an MVO");
			alert(mvoErr.message);
			reject(mvoErr);
			return false;
		}

		// access msg.sender and verifying contract address
		const sender = accounts[0];
		const enshContract = contracts["EnshroudProtocol"];
		const enshAddress = enshContract.options.address;

		// generate reply key and the payload we must sign
		var replyKey = '';
		var payload = '';
		if (!mvoComm.encrypted) {
			// old version, for use without encryption (passed as POST param)
			payload = 'walletDownload={"chainId":"' + chId
						+ '","sender":"' + sender + '","IDList":[]}';

			// send plain data unencrypted and unsigned
			mvoComm.sendToMVO(payload, props.parseReplyMethod);
		}
		else {
			// generate an AES key for the MVO to use to encrypt normal replies
			mvoComm.generateAesKey();
			// NB: generateAesKey() stored raw key in mvoComm.replyKeyB64
			replyKey = mvoComm.decryptKey;

			// define eth_signTypedData_v4 parameters
			const msgParams = JSON.stringify({
				// EIP-712 domain info (depends upon chId scan URL for display)
				domain: {
					chainId: chId,
					name: 'Enshroud',
					verifyingContract: enshAddress,
					version: '1',
				},
				// descriptive info on what's being signed and for whom
				message: {
					contents: 'Send encrypted request to download the eNFTs and keys in your wallet',
					to: {
						MVOId: mvoComm.mvo,
						URL: mvoComm.mvoURL,
					},
					requestJson: {
						walletDownload: {
							chainId: `${chId}`,
							sender: sender,
							IDList: [],
							replyKey: replyKey,
						},
					},
				},
				primaryType: 'Request',
				types: {
					// the domain the contract is hosted on
					EIP712Domain: [
						{ name: 'chainId', type: 'uint256' },
						{ name: 'name', type: 'string' },
						{ name: 'verifyingContract', type: 'address' },
						{ name: 'version', type: 'string' },
					],
					// refer to primaryType
					Request: [
						{ name: 'contents', type: 'string' },
						{ name: 'to', type: 'MVO' },
						{ name: 'requestJson', type: 'WalletDownload' },
					],
					// not an EIP712Domain definition
					MVO: [
						{ name: 'MVOId', type: 'string' },
						{ name: 'URL', type: 'string' },
					],
					// not an EIP712Domain definition
					WalletDownload: [
						{ name: 'walletDownload', type: 'Payload' },
					],
					// not an EIP712Domain definition
					Payload: [
						{ name: 'chainId', type: 'string' },
						{ name: 'sender', type: 'address' },
						{ name: 'IDList', type: 'string[]' },
						{ name: 'replyKey', type: 'string' },
					],
				},
			});
			const method = 'eth_signTypedData_v4';
			var params = [sender, msgParams];

			// now obtain signature on params in a EIP-712 compatible way
			var userSig;
			await web3.currentProvider.sendAsync(
				{
					method,
					params,
					from: sender,
				},
				function (err, result) {
					if (err) console.dir(err);
					if (result.error) alert(result.error.message);
					if (result.error) console.error('ERROR', result.error);
					userSig = result.result;
					if (userSig === undefined) {
						let sigErr
							= new Error("Error building EIP712 signature");
						reject(sigErr);
						return false;
					}

					// append signature to the arguments
					const allArgs = JSON.parse(msgParams);
					allArgs.signature = userSig;

					// encrypt + send the message to the MVO, passing callback
					mvoComm.sendToMVO(JSON.stringify(allArgs),
									  props.parseReplyMethod);
					// NB: even if sendToMVO() fails, we still want to resolve
					resolve(true);
				}
			);
		}

		// clear the existing eNFT list
		eNFTs.splice(0, eNFTs.length);
		return true;
	}

	// render the actual Home Page
	return (
		<div className="HomePage">
		<Container fluid align="center">
			<h2>Welcome to Enshroud!</h2>
			<br/>
			<p className="text-lead">
				For explainer info on Enshroud:
				<Button className="btn btn-info btn-sm m-2"
					title="Go to separate page explaining what Enshroud does"
					onClick={() => props.onSelect("explainer")}
					id="goExplainer"
				>
					What Is Enshroud?
				</Button>
				<br/>
			</p>
			<hr/>
		</Container>

		{ /* table for assets on deposit */ }
		<Container fluid>
			<br/>
			<Card>
				<Card.Body>
					<Card.Title>Enshroud Home Page</Card.Title>
					<Card.Text>
						The first table below shows the assets you have on
						deposit, available for eNFT Minting.  Use
						the <b>Refresh Your Deposits</b> button
						under the table heading to populate the table.
						<br/><br/>
						To add a completely new token to the list, go to the
						Asset Config page.
					</Card.Text>
					<Card.Text>
						The second table displays the eNFTs in your wallet.
						You must use the <b>Refresh</b> button to populate
						the list.
					</Card.Text>
					<Card.Link href="#" title="Go to eNFT Minting page"
				 		onClick={() => props.onSelect('mintENFTs')}
						id="goMinting" role="button"
					>
						eNFT Minting
					</Card.Link>
					<Card.Link href="#" title="Go to Asset Config page"
				 		onClick={() => props.onSelect('assetConfig')}
						id="goConfig" role="button"
					>
						Asset Config
					</Card.Link>
				</Card.Body>
			</Card>
			<br/>
			<Table striped bordered hover responsive>
				<caption className="caption-top">
					Token assets you have on deposit (available for minting):
				</caption>
				<thead>
					<tr align="center" key="hdr-deposit-assets">
						<th scope="col"
							title="Contract address shown in hover text">
							Asset
						</th>
						<th scope="col"
							title="Shown in ethers units for readability">
							Balance (1e-18)</th>
						<th scope="col"
							title="Value in USD at market prices, if available">
							Value in USD
						</th>
					</tr>
				</thead>

				{ /* this supplies the <tbody/> */ }
				{chainConn == null || contracts == null ?
					<tbody><tr align="center"><td colSpan={3}><NoticeWrongNetwork /></td></tr></tbody>
					: <DepositTable />
				}
			</Table>
			<br/><br/>
		</Container>
		<br/>

		{ /* Refresh button for balances */ }
		<Container fluid>
			<h4>Your Wallet eNFT Balances:
				<LoadingButton
					variant="primary"
					buttonStyle="m-3"
					netMethod={(resolve, reject) => sendWalletReqToMVO(resolve, reject)}
					buttonTitle="This fetches the eNFT list from an MVO, and populates the eNFT balances table below"
					buttonText="Refresh"
					buttonIcon="images/download.svg"
				/>
				<i>(signature required)</i>
			</h4>
		</Container>

		{ /* table for eNFTs in wallet */ }
		<Container fluid>
			<Table striped bordered hover responsive variant="dark">
				<caption className="caption-top">
					eNFT balances, available for Spend or Burn:<br/>
					<i>
						(Available can be less if one or more eNFTs is
						unconfirmed)
					</i>
				</caption>
				<thead>
					<tr align="center" valign="middle">
						<th scope="col"
							title="Click the button link to examine the contract">
							Asset / Token
						</th>
						<th scope="col"
							title="Amounts shown in ethers units (1e-18) for readability">
							Balance <i>(available)</i>
						</th>
						<th scope="col"
							title="Value of available balance in USD at market prices, if known">
							Value in USD
						</th>
					</tr>
				</thead>

				{ /* this supplies the <tbody/> */}
				<ENFTTable
					details={eNFTTableProps}
					marketPrices={marketPrices}
				/>
			</Table>
			<br/><br/>
		</Container>
		<hr/>

		{ /* Receipts button for history */ }
		<br/>
		<Container fluid>
			<h4>Your Transaction History:
				<Button id="goReceipts" variant="secondary" className="m-3"
					onClick={() => props.onSelect('receipts')}
					title="Switch to Transaction History page"
				>
					Receipts
						<Image src="images/receipt.svg" className="p-2"
							fluid rounded height="40" width="40" />
				</Button>
			</h4>
			<br/>
		</Container>
		<hr/>
		<br/>
		</div>
	);
}

export default HomePage;
