260 lines
8.8 KiB
JavaScript
260 lines
8.8 KiB
JavaScript
import {
|
|
Box,
|
|
Link,
|
|
SvgIcon,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableContainer,
|
|
TableHead,
|
|
TableRow,
|
|
Typography,
|
|
useMediaQuery
|
|
} from "@mui/material";
|
|
import { NavLink } from "react-router-dom";
|
|
import ArrowUp from "../../../assets/icons/arrow-up.svg?react";
|
|
|
|
import BondDiscount from "./BondDiscount";
|
|
import BondDuration from "./BondDuration";
|
|
import BondInfoText from "./BondInfoText";
|
|
import BondPrice from "./BondPrice";
|
|
|
|
import { useScreenSize } from "../../../hooks/useScreenSize";
|
|
import { sortBondsByDiscount, formatNumber } from "../../../helpers";
|
|
import TokenStack from "../../../components/TokenStack/TokenStack";
|
|
import { TertiaryButton } from "../../../components/Button";
|
|
|
|
export const BondList = ({ bonds, secondsTo, chainId }) => {
|
|
const isSmallScreen = useScreenSize("md");
|
|
|
|
if (bonds.length === 0) {
|
|
return (
|
|
<Box display="flex" justifyContent="center">
|
|
<Typography variant="h4">No active bonds</Typography>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
if (isSmallScreen) {
|
|
return (
|
|
<>
|
|
<Box my="24px" textAlign="center">
|
|
<BondInfoText />
|
|
</Box>
|
|
|
|
{sortBondsByDiscount(bonds).map(bond => (
|
|
<BondCard key={bond.id} secondsTo={secondsTo} bond={bond} />
|
|
))}
|
|
</>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<BondTable>
|
|
{sortBondsByDiscount(bonds).map(bond => (
|
|
<BondRow key={bond.id} bond={bond} secondsTo={secondsTo} />
|
|
))}
|
|
</BondTable>
|
|
|
|
<Box mt="24px" textAlign="center" width="70%" mx="auto">
|
|
<BondInfoText />
|
|
</Box>
|
|
</>
|
|
);
|
|
};
|
|
|
|
const BondCard = ({ bond, secondsTo }) => {
|
|
const quoteTokenName = bond.quoteToken.name;
|
|
const baseTokenName = bond.baseToken.name;
|
|
|
|
return (
|
|
<Box key={bond.id} id={bond.id + `--bond`} mt="32px">
|
|
<Box display="flex" alignItems="center">
|
|
<TokenStack tokens={bond.quoteToken.icons} />
|
|
|
|
<Box display="flex" flexDirection="column" ml="8px">
|
|
<Typography>{bond.quoteToken.name}</Typography>
|
|
|
|
<Link href={bond.quoteToken.purchaseUrl} target="_blank" rel="noopener noreferrer">
|
|
<Box display="flex" alignItems="center">
|
|
<Typography>Get Asset</Typography>
|
|
|
|
<Box ml="4px">
|
|
<SvgIcon component={ArrowUp} />
|
|
</Box>
|
|
</Box>
|
|
</Link>
|
|
</Box>
|
|
</Box>
|
|
|
|
<Box display="flex" justifyContent="space-between" mt="16px">
|
|
<Typography>Price</Typography>
|
|
|
|
<Typography component={'span'}>
|
|
{bond.isSoldOut ? (
|
|
"--"
|
|
) : (
|
|
<BondPrice price={bond.price.inUsd} />
|
|
)}
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Box display="flex" justifyContent="space-between" mt="8px">
|
|
<Typography>Discount</Typography>
|
|
<Typography component={'span'}>{
|
|
bond.isSoldOut
|
|
? "--"
|
|
: <BondDiscount discount={bond.discount} />
|
|
}</Typography>
|
|
</Box>
|
|
|
|
<Box display="flex" justifyContent="space-between" mt="8px">
|
|
<Typography>Max Payout</Typography>
|
|
|
|
<Typography>
|
|
{`${payoutTokenCapacity(bond)}${
|
|
bond.baseToken.name !== bond.quoteToken.name ? ` (${quoteTokenCapacity(bond)})` : ``
|
|
}`}
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Box display="flex" justifyContent="space-between" mt="8px">
|
|
<Typography>Duration</Typography>
|
|
|
|
<Typography component={'span'}>
|
|
<BondDuration secondsTo={secondsTo} duration={bond.duration} />
|
|
</Typography>
|
|
</Box>
|
|
|
|
<Box mt="16px">
|
|
<Link
|
|
component={NavLink}
|
|
to={`/bonds/${bond.id}`}
|
|
>
|
|
<TertiaryButton fullWidth>
|
|
Bond
|
|
</TertiaryButton>
|
|
</Link>
|
|
</Box>
|
|
</Box>
|
|
);
|
|
};
|
|
|
|
const BondTable = ({ children }) => (
|
|
<TableContainer>
|
|
<Table aria-label="Available bonds" style={{ tableLayout: "fixed" }}>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell style={{ padding: "8px 0", width: "162px" }}>Bond</TableCell>
|
|
<TableCell style={{ padding: "8px 0", width: "146px" }}>Price</TableCell>
|
|
<TableCell style={{ padding: "8px 0", width: "92px" }}>Discount</TableCell>
|
|
<TableCell style={{ padding: "8px 0" }}>Max Payout</TableCell>
|
|
<TableCell style={{ padding: "8px 0" }}>Duration</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
|
|
<TableBody>{children}</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
);
|
|
|
|
const quoteTokenCapacity = (bond) => {
|
|
const quoteTokenCapacity = bond.maxPayout.inQuoteToken.lt(bond.capacity.inQuoteToken)
|
|
? bond.maxPayout.inQuoteToken
|
|
: bond.capacity.inQuoteToken;
|
|
return `${formatNumber(quoteTokenCapacity, 4)} ${bond.quoteToken.name}`;
|
|
};
|
|
|
|
const payoutTokenCapacity = (bond) => {
|
|
const payoutTokenCapacity = bond.maxPayout.inBaseToken.lt(bond.capacity.inBaseToken)
|
|
? bond.maxPayout.inBaseToken
|
|
: bond.capacity.inBaseToken;
|
|
return `${formatNumber(payoutTokenCapacity, 4)} FTSO`;
|
|
};
|
|
|
|
const BondRow = ({ bond, secondsTo }) => {
|
|
const quoteTokenName = bond.quoteToken.name;
|
|
const baseTokenName = bond.baseToken.name;
|
|
|
|
return (
|
|
<TableRow id={bond.id + `--bond`} data-testid={bond.id + `--bond`}>
|
|
<TableCell style={{ padding: "8px 0" }}>
|
|
<TokenIcons token={bond.quoteToken} />
|
|
</TableCell>
|
|
|
|
<TableCell style={{ padding: "8px 0" }}>
|
|
<Typography>
|
|
{bond.isSoldOut ? (
|
|
"--"
|
|
) : (
|
|
<BondPrice price={bond.price.inUsd} />
|
|
)}
|
|
</Typography>
|
|
</TableCell>
|
|
|
|
<TableCell style={{ padding: "8px 0" }}>
|
|
<Typography component="div">
|
|
{bond.isSoldOut ? "--" : <BondDiscount discount={bond.discount} />}
|
|
</Typography>
|
|
</TableCell>
|
|
|
|
<TableCell style={{ padding: "8px 0" }}>
|
|
<Box display="flex" flexDirection={"column"}>
|
|
<Typography style={{ lineHeight: "20px" }}>{payoutTokenCapacity(bond)}</Typography>
|
|
{bond.baseToken.name !== bond.quoteToken.name && (
|
|
<Typography color="textSecondary" style={{ fontSize: "12px", fontWeight: 400, lineHeight: "18px" }}>
|
|
{quoteTokenCapacity(bond)}
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
</TableCell>
|
|
|
|
<TableCell style={{ padding: "8px 0" }}>
|
|
<Typography>{
|
|
bond.isSoldOut
|
|
? "--"
|
|
: <BondDuration secondsTo={secondsTo} duration={bond.duration} />
|
|
}</Typography>
|
|
</TableCell>
|
|
|
|
<TableCell style={{ padding: "8px 0" }}>
|
|
<Link
|
|
component={NavLink}
|
|
to={`/bonds/${bond.id}`}
|
|
>
|
|
<TertiaryButton fullWidth disabled={bond.isSoldOut}>
|
|
{bond.isSoldOut ? "Sold Out" : `Bond`}
|
|
</TertiaryButton>
|
|
</Link>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
};
|
|
|
|
const TokenIcons = ({ token, chainId, explorer }) => (
|
|
<Box display="flex" alignItems="center">
|
|
<TokenStack tokens={token.icons} />
|
|
|
|
<Box display="flex" flexDirection="column" ml="16px">
|
|
<Typography style={{ fontSize: "12px", fontWeight: 600, lineHeight: "18px" }}>{token.name}</Typography>
|
|
|
|
<Link
|
|
color="primary"
|
|
target="_blank"
|
|
href={explorer ? `https://etherscan.io/token/${token.addresses[chainId]}` : token.purchaseUrl}
|
|
>
|
|
<Box display="flex" alignItems="center">
|
|
<Typography style={{ fontSize: "12px", lineHeight: "18px" }}>
|
|
{explorer ? `Explorer` : `Get Asset`}
|
|
</Typography>
|
|
|
|
<Box ml="4px">
|
|
<SvgIcon component={ArrowUp} />
|
|
</Box>
|
|
</Box>
|
|
</Link>
|
|
</Box>
|
|
</Box>
|
|
);
|