add sell and buy orders list
This commit is contained in:
@@ -38,12 +38,12 @@ export const Pool = ({ pool }: PoolProps) => {
|
|||||||
}: ${JSON.stringify(totalStaked)}`
|
}: ${JSON.stringify(totalStaked)}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const aprValue = getApr(
|
const aprValue = getApr({
|
||||||
stakingPrice,
|
rewardTokenPrice: stakingPrice,
|
||||||
earningPrice,
|
stakingTokenPrice: earningPrice,
|
||||||
totalStaked,
|
tokenPerBlock: totalStaked,
|
||||||
parseFloat(pool.tokenPerBlock) / 1e-18
|
totalStaked: parseFloat(pool.tokenPerBlock) / 1e-18,
|
||||||
);
|
});
|
||||||
|
|
||||||
if (aprValue) {
|
if (aprValue) {
|
||||||
setApr({
|
setApr({
|
||||||
|
|||||||
@@ -0,0 +1,187 @@
|
|||||||
|
import React, { useState } from "react";
|
||||||
|
import type { FC } from "react";
|
||||||
|
import { graphql } from "babel-plugin-relay/macro";
|
||||||
|
import { useFragment } from "react-relay";
|
||||||
|
import { BigNumber } from "bignumber.js";
|
||||||
|
import cx from "classnames";
|
||||||
|
|
||||||
|
import { useCurrentUser } from "../../../../contexts/UserProvider";
|
||||||
|
import { Unauthenticated } from "../../../../messages/Unauthenticated";
|
||||||
|
import type { CreateExchangeOrderModal_fiatBalances$key } from "./__generated__/CreateExchangeOrderModal_fiatBalances.graphql";
|
||||||
|
import type { CreateExchangeOrderModal_balances$key } from "./__generated__/CreateExchangeOrderModal_balances.graphql";
|
||||||
|
|
||||||
|
const tabBaseStyles =
|
||||||
|
"w-full text-base font-bold text-black px-4 py-2 focus:ring-blue-500";
|
||||||
|
|
||||||
|
const selectedTabStyles =
|
||||||
|
"bg-blue-600 hover:bg-blue-700 rounded-l-frounded-full text-white";
|
||||||
|
|
||||||
|
const inputBaseStyles =
|
||||||
|
"rounded-lg border-transparent flex-1 appearance-none border border-gray-300 w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent mb-3";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
fiatBalancesRefs: CreateExchangeOrderModal_fiatBalances$key;
|
||||||
|
balancesRefs: CreateExchangeOrderModal_balances$key;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CreateExchangeOrderModal: FC<Props> = ({
|
||||||
|
fiatBalancesRefs,
|
||||||
|
balancesRefs,
|
||||||
|
}) => {
|
||||||
|
const { isAuthenticated } = useCurrentUser();
|
||||||
|
const [exchangeOption, setExchangeOption] = useState<"BUY" | "SELL">("BUY");
|
||||||
|
const [cryptoDock, setCryptoDock] = useState<string>("0");
|
||||||
|
const [fiatDock, setFiatDock] = useState<string>("0.00");
|
||||||
|
|
||||||
|
const fiatBalances = useFragment<CreateExchangeOrderModal_fiatBalances$key>(
|
||||||
|
graphql`
|
||||||
|
fragment CreateExchangeOrderModal_fiatBalances on FiatBalanceConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
amountCents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
fiatBalancesRefs
|
||||||
|
);
|
||||||
|
|
||||||
|
const balances = useFragment<CreateExchangeOrderModal_balances$key>(
|
||||||
|
graphql`
|
||||||
|
fragment CreateExchangeOrderModal_balances on BalanceConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
amount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
balancesRefs
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isAuthenticated) return <Unauthenticated />;
|
||||||
|
const [crypto] = balances.edges;
|
||||||
|
const [fiat] = fiatBalances.edges;
|
||||||
|
|
||||||
|
const avaliableCrypto = new BigNumber(crypto.node.amount);
|
||||||
|
const avaliableFiat = (
|
||||||
|
fiat.node.amountCents ? fiat.node.amountCents / 100 : 0
|
||||||
|
).toFixed(2);
|
||||||
|
|
||||||
|
const handleSellTabClick = () => {
|
||||||
|
setExchangeOption("SELL");
|
||||||
|
setCryptoDock("0");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBuyTabClick = () => {
|
||||||
|
setExchangeOption("BUY");
|
||||||
|
setCryptoDock("0");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCryptoAmountChange = ({
|
||||||
|
currentTarget: { value },
|
||||||
|
}: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const newCryptoAmount = new BigNumber(value);
|
||||||
|
|
||||||
|
if (newCryptoAmount.isLessThanOrEqualTo(avaliableCrypto)) {
|
||||||
|
setCryptoDock(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFiatAmountChange = ({
|
||||||
|
currentTarget: { value },
|
||||||
|
}: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const newFiatAmount = Number(value);
|
||||||
|
|
||||||
|
if (Number(avaliableFiat) >= newFiatAmount) {
|
||||||
|
setFiatDock(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMaxFiatDockButton = () => {
|
||||||
|
setFiatDock(avaliableFiat);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMaxCryptoButton = () => {
|
||||||
|
setCryptoDock(avaliableCrypto.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-lg">
|
||||||
|
<div className="flex items-center bg-white rounded-full border border-gray-200 mb-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={cx(
|
||||||
|
tabBaseStyles,
|
||||||
|
"rounded-full",
|
||||||
|
exchangeOption === "BUY" && selectedTabStyles
|
||||||
|
)}
|
||||||
|
onClick={handleBuyTabClick}
|
||||||
|
>
|
||||||
|
Comprar
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={cx(
|
||||||
|
tabBaseStyles,
|
||||||
|
"rounded-full",
|
||||||
|
exchangeOption === "SELL" && selectedTabStyles
|
||||||
|
)}
|
||||||
|
onClick={handleSellTabClick}
|
||||||
|
>
|
||||||
|
Vender
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form className="bg-white p-4 rounded-2xl border border-gray-200">
|
||||||
|
<span className="mb-2">
|
||||||
|
{exchangeOption === "SELL" ? "CAKE" : "BRL"} disponível:{" "}
|
||||||
|
{exchangeOption === "SELL" ? crypto.node.amount : avaliableFiat}
|
||||||
|
</span>
|
||||||
|
<div className="flex flex-row">
|
||||||
|
{exchangeOption === "BUY" ? (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
className={cx(inputBaseStyles)}
|
||||||
|
type="number"
|
||||||
|
value={fiatDock}
|
||||||
|
onChange={handleFiatAmountChange}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={fiatDock === avaliableFiat}
|
||||||
|
className="flex items-center mb-3 ml-3 font-bold rounded-full text-red-500"
|
||||||
|
onClick={handleMaxFiatDockButton}
|
||||||
|
>
|
||||||
|
Max
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<input
|
||||||
|
className={cx(inputBaseStyles)}
|
||||||
|
type="number"
|
||||||
|
value={cryptoDock}
|
||||||
|
onChange={handleCryptoAmountChange}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={avaliableCrypto.isEqualTo(cryptoDock)}
|
||||||
|
className="flex items-center mb-3 ml-3 font-bold rounded-full text-red-500"
|
||||||
|
onClick={handleMaxCryptoButton}
|
||||||
|
>
|
||||||
|
Max
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="cursor-pointer py-2 px-4 bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white w-full transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
{exchangeOption === "BUY" ? "Comprar" : "Vender"} CAKE
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type CreateExchangeOrderModal_balances = {
|
||||||
|
readonly edges: ReadonlyArray<{
|
||||||
|
readonly node: {
|
||||||
|
readonly amount: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
readonly " $refType": "CreateExchangeOrderModal_balances";
|
||||||
|
};
|
||||||
|
export type CreateExchangeOrderModal_balances$data = CreateExchangeOrderModal_balances;
|
||||||
|
export type CreateExchangeOrderModal_balances$key = {
|
||||||
|
readonly " $data"?: CreateExchangeOrderModal_balances$data;
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"CreateExchangeOrderModal_balances">;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const node: ReaderFragment = {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "CreateExchangeOrderModal_balances",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BalanceEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Balance",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amount",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "BalanceConnection",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
(node as any).hash = '42aad1bd63f1135b4d99ec236cd945b5';
|
||||||
|
export default node;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type CreateExchangeOrderModal_fiatBalances = {
|
||||||
|
readonly edges: ReadonlyArray<{
|
||||||
|
readonly node: {
|
||||||
|
readonly amountCents: number;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
readonly " $refType": "CreateExchangeOrderModal_fiatBalances";
|
||||||
|
};
|
||||||
|
export type CreateExchangeOrderModal_fiatBalances$data = CreateExchangeOrderModal_fiatBalances;
|
||||||
|
export type CreateExchangeOrderModal_fiatBalances$key = {
|
||||||
|
readonly " $data"?: CreateExchangeOrderModal_fiatBalances$data;
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"CreateExchangeOrderModal_fiatBalances">;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const node: ReaderFragment = {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "CreateExchangeOrderModal_fiatBalances",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "FiatBalanceEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "FiatBalance",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amountCents",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "FiatBalanceConnection",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
(node as any).hash = 'b3a734bd9e34e02aacfa42b6b95776a5';
|
||||||
|
export default node;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./CreateExchangeOrderModal";
|
||||||
@@ -1,176 +1,47 @@
|
|||||||
import React, { useState } from "react";
|
/* eslint-disable relay/must-colocate-fragment-spreads */
|
||||||
import type { FC } from "react";
|
|
||||||
import { graphql } from "babel-plugin-relay/macro";
|
import { graphql } from "babel-plugin-relay/macro";
|
||||||
|
import React, { useState } from "react";
|
||||||
import { useLazyLoadQuery } from "react-relay";
|
import { useLazyLoadQuery } from "react-relay";
|
||||||
import { BigNumber } from "bignumber.js";
|
|
||||||
import cx from "classnames";
|
|
||||||
|
|
||||||
import { useCurrentUser } from "../../../contexts/UserProvider";
|
import { CreateExchangeOrderModal } from "./CreateExchangeOrderModel";
|
||||||
import { Unauthenticated } from "../../../messages/Unauthenticated";
|
import { ExchangeHistory } from "./ExchangeHistory";
|
||||||
import type { ExchangeQuery } from "./__generated__/ExchangeQuery.graphql";
|
import type { ExchangeQuery } from "./__generated__/ExchangeQuery.graphql";
|
||||||
|
|
||||||
const tabBaseStyles =
|
export const Exchange = () => {
|
||||||
"w-full text-base font-bold text-black px-4 py-2 focus:ring-blue-500";
|
const [modelOpen] = useState<boolean>(false);
|
||||||
|
|
||||||
const selectedTabStyles =
|
const data = useLazyLoadQuery<ExchangeQuery>(
|
||||||
"bg-blue-600 hover:bg-blue-700 rounded-l-frounded-full text-white";
|
|
||||||
|
|
||||||
const inputBaseStyles =
|
|
||||||
"rounded-lg border-transparent flex-1 appearance-none border border-gray-300 w-full py-2 px-4 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent mb-3";
|
|
||||||
|
|
||||||
export const Exchange: FC = () => {
|
|
||||||
const { isAuthenticated } = useCurrentUser();
|
|
||||||
const [exchangeOption, setExchangeOption] = useState<"BUY" | "SELL">("BUY");
|
|
||||||
const [cryptoDock, setCryptoDock] = useState<string>("0");
|
|
||||||
const [fiatDock, setFiatDock] = useState<string>("0.00");
|
|
||||||
|
|
||||||
const { balances, fiatBalances } = useLazyLoadQuery<ExchangeQuery>(
|
|
||||||
graphql`
|
graphql`
|
||||||
query ExchangeQuery {
|
query ExchangeQuery {
|
||||||
fiatBalances {
|
fiatBalances {
|
||||||
edges {
|
...CreateExchangeOrderModal_fiatBalances
|
||||||
node {
|
|
||||||
amountCents
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
balances {
|
balances {
|
||||||
edges {
|
...CreateExchangeOrderModal_balances
|
||||||
node {
|
|
||||||
amount
|
|
||||||
}
|
}
|
||||||
|
buyCryptoOrders {
|
||||||
|
...ExchangeHistory_buyCryptoOrders
|
||||||
}
|
}
|
||||||
|
sellCryptoOrders {
|
||||||
|
...ExchangeHistory_sellCryptoOrders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isAuthenticated) return <Unauthenticated />;
|
|
||||||
const [crypto] = balances.edges;
|
|
||||||
const [fiat] = fiatBalances.edges;
|
|
||||||
|
|
||||||
const avaliableCrypto = new BigNumber(crypto.node.amount);
|
|
||||||
const avaliableFiat = (
|
|
||||||
fiat.node.amountCents ? fiat.node.amountCents / 100 : 0
|
|
||||||
).toFixed(2);
|
|
||||||
|
|
||||||
const handleSellTabClick = () => {
|
|
||||||
setExchangeOption("SELL");
|
|
||||||
setCryptoDock("0");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBuyTabClick = () => {
|
|
||||||
setExchangeOption("BUY");
|
|
||||||
setCryptoDock("0");
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCryptoAmountChange = ({
|
|
||||||
currentTarget: { value },
|
|
||||||
}: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const newCryptoAmount = new BigNumber(value);
|
|
||||||
|
|
||||||
if (newCryptoAmount.isLessThanOrEqualTo(avaliableCrypto)) {
|
|
||||||
setCryptoDock(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleFiatAmountChange = ({
|
|
||||||
currentTarget: { value },
|
|
||||||
}: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const newFiatAmount = Number(value);
|
|
||||||
|
|
||||||
if (Number(avaliableFiat) >= newFiatAmount) {
|
|
||||||
setFiatDock(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMaxFiatDockButton = () => {
|
|
||||||
setFiatDock(avaliableFiat);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleMaxCryptoButton = () => {
|
|
||||||
setCryptoDock(avaliableCrypto.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid place-items-center w-full">
|
<div className="w-full">
|
||||||
<div className="max-w-lg">
|
<ExchangeHistory
|
||||||
<div className="flex items-center bg-white rounded-full border border-gray-200 mb-3">
|
sellCryptoOrdersRefs={data.sellCryptoOrders}
|
||||||
<button
|
buyCryptoOrdersRefs={data.buyCryptoOrders}
|
||||||
type="button"
|
|
||||||
className={cx(
|
|
||||||
tabBaseStyles,
|
|
||||||
"rounded-full",
|
|
||||||
exchangeOption === "BUY" && selectedTabStyles
|
|
||||||
)}
|
|
||||||
onClick={handleBuyTabClick}
|
|
||||||
>
|
|
||||||
Comprar
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={cx(
|
|
||||||
tabBaseStyles,
|
|
||||||
"rounded-full",
|
|
||||||
exchangeOption === "SELL" && selectedTabStyles
|
|
||||||
)}
|
|
||||||
onClick={handleSellTabClick}
|
|
||||||
>
|
|
||||||
Vender
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<form className="bg-white p-4 rounded-2xl border border-gray-200">
|
|
||||||
<span className="mb-2">
|
|
||||||
{exchangeOption === "SELL" ? "CAKE" : "BRL"} disponível:{" "}
|
|
||||||
{exchangeOption === "SELL" ? crypto.node.amount : avaliableFiat}
|
|
||||||
</span>
|
|
||||||
<div className="flex flex-row">
|
|
||||||
{exchangeOption === "BUY" ? (
|
|
||||||
<>
|
|
||||||
<input
|
|
||||||
className={cx(inputBaseStyles)}
|
|
||||||
type="number"
|
|
||||||
value={fiatDock}
|
|
||||||
onChange={handleFiatAmountChange}
|
|
||||||
/>
|
/>
|
||||||
<button
|
{modelOpen && (
|
||||||
type="button"
|
<CreateExchangeOrderModal
|
||||||
disabled={fiatDock === avaliableFiat}
|
balancesRefs={data.balances}
|
||||||
className="flex items-center mb-3 ml-3 font-bold rounded-full text-red-500"
|
fiatBalancesRefs={data.fiatBalances}
|
||||||
onClick={handleMaxFiatDockButton}
|
|
||||||
>
|
|
||||||
Max
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<input
|
|
||||||
className={cx(inputBaseStyles)}
|
|
||||||
type="number"
|
|
||||||
value={cryptoDock}
|
|
||||||
onChange={handleCryptoAmountChange}
|
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={avaliableCrypto.isEqualTo(cryptoDock)}
|
|
||||||
className="flex items-center mb-3 ml-3 font-bold rounded-full text-red-500"
|
|
||||||
onClick={handleMaxCryptoButton}
|
|
||||||
>
|
|
||||||
Max
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
|
||||||
className="cursor-pointer py-2 px-4 bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 focus:ring-offset-blue-200 text-white w-full transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
{exchangeOption === "BUY" ? "Comprar" : "Vender"} CAKE
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
import { graphql } from "babel-plugin-relay/macro";
|
||||||
|
import type { FC } from "react";
|
||||||
|
import React from "react";
|
||||||
|
import { useFragment } from "react-relay";
|
||||||
|
|
||||||
|
import { centsToUnit } from "../../../../utils/fiatMoney";
|
||||||
|
import type { CryptoExchangeOrderProps } from "./components/CryptoExchangeOrder";
|
||||||
|
import { CryptoExchangeOrder } from "./components/CryptoExchangeOrder";
|
||||||
|
import type {
|
||||||
|
ExchangeHistory_buyCryptoOrders$key,
|
||||||
|
ProcessStatus,
|
||||||
|
} from "./__generated__/ExchangeHistory_buyCryptoOrders.graphql";
|
||||||
|
import type { ExchangeHistory_sellCryptoOrders$key } from "./__generated__/ExchangeHistory_sellCryptoOrders.graphql";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
buyCryptoOrdersRefs: ExchangeHistory_buyCryptoOrders$key;
|
||||||
|
sellCryptoOrdersRefs: ExchangeHistory_sellCryptoOrders$key;
|
||||||
|
};
|
||||||
|
|
||||||
|
type SellOrBuyOrder = {
|
||||||
|
readonly node: {
|
||||||
|
readonly id: string;
|
||||||
|
readonly status: ProcessStatus;
|
||||||
|
readonly paidAmount: string;
|
||||||
|
readonly receivedAmountCents: number | null;
|
||||||
|
readonly createdAt: unknown;
|
||||||
|
readonly paidAmountCents: number;
|
||||||
|
readonly receivedAmount: string | null;
|
||||||
|
readonly currency: {
|
||||||
|
readonly name: string;
|
||||||
|
};
|
||||||
|
readonly __typename: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ExchangeHistory: FC<Props> = ({
|
||||||
|
buyCryptoOrdersRefs,
|
||||||
|
sellCryptoOrdersRefs,
|
||||||
|
}) => {
|
||||||
|
const buyCryptoOrders = useFragment<ExchangeHistory_buyCryptoOrders$key>(
|
||||||
|
graphql`
|
||||||
|
fragment ExchangeHistory_buyCryptoOrders on BuyCryptoOrderConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
createdAt
|
||||||
|
paidAmountCents
|
||||||
|
receivedAmount
|
||||||
|
currency {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
buyCryptoOrdersRefs
|
||||||
|
);
|
||||||
|
|
||||||
|
const sellCryptoOrders = useFragment<ExchangeHistory_sellCryptoOrders$key>(
|
||||||
|
graphql`
|
||||||
|
fragment ExchangeHistory_sellCryptoOrders on SellCryptoOrderConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
paidAmount
|
||||||
|
receivedAmountCents
|
||||||
|
createdAt
|
||||||
|
currency {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
sellCryptoOrdersRefs
|
||||||
|
);
|
||||||
|
|
||||||
|
const allResults = [...buyCryptoOrders.edges, ...sellCryptoOrders.edges];
|
||||||
|
|
||||||
|
const allResultsOrdeneds = allResults.sort((item1, item2) => {
|
||||||
|
return (
|
||||||
|
new Date(item1.node.createdAt as string).getTime() -
|
||||||
|
new Date(item2.node.createdAt as string).getTime()
|
||||||
|
);
|
||||||
|
}) as SellOrBuyOrder[];
|
||||||
|
|
||||||
|
const orderRows: Array<(CryptoExchangeOrderProps & { id: string }) | null> =
|
||||||
|
allResultsOrdeneds.map((edge) => {
|
||||||
|
const node = edge?.node;
|
||||||
|
|
||||||
|
if (node?.__typename === "SellCryptoOrder") {
|
||||||
|
return {
|
||||||
|
id: node.id,
|
||||||
|
payed: `${node.paidAmount} ${node.currency.name}`,
|
||||||
|
received: `${centsToUnit(node.receivedAmountCents)} BRL`,
|
||||||
|
createdAt: new Date(node.createdAt as string).toLocaleString(),
|
||||||
|
kind: node.__typename,
|
||||||
|
status: node.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node?.__typename === "BuyCryptoOrder") {
|
||||||
|
return {
|
||||||
|
id: node.id,
|
||||||
|
payed: `${centsToUnit(node.paidAmountCents)} BRL`,
|
||||||
|
received: `${node.receivedAmount} ${node.currency.name}`,
|
||||||
|
createdAt: new Date(node.createdAt as string).toLocaleString(),
|
||||||
|
kind: node.__typename,
|
||||||
|
status: node.status,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 sm:px-8">
|
||||||
|
<div className="py-8">
|
||||||
|
<div className="-mx-4 sm:-mx-8 px-4 sm:px-8 py-4 overflow-x-auto">
|
||||||
|
<div className="inline-block min-w-full shadow rounded-lg overflow-hidden">
|
||||||
|
<table className="min-w-full leading-normal">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
||||||
|
>
|
||||||
|
Valor pago
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
||||||
|
>
|
||||||
|
Valor recebido
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
||||||
|
>
|
||||||
|
Criado em
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
className="px-5 py-3 bg-white border-b border-gray-200 text-gray-800 text-left text-sm uppercase font-normal"
|
||||||
|
>
|
||||||
|
Status
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{orderRows.map((order) => {
|
||||||
|
return <CryptoExchangeOrder key={order?.id} {...order} />;
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type ProcessStatus = "CANCELED" | "COMPLETED" | "PROCESSING" | "%future added value";
|
||||||
|
export type ExchangeHistory_buyCryptoOrders = {
|
||||||
|
readonly edges: ReadonlyArray<{
|
||||||
|
readonly node: {
|
||||||
|
readonly id: string;
|
||||||
|
readonly status: ProcessStatus;
|
||||||
|
readonly createdAt: unknown;
|
||||||
|
readonly paidAmountCents: number;
|
||||||
|
readonly receivedAmount: string | null;
|
||||||
|
readonly currency: {
|
||||||
|
readonly name: string;
|
||||||
|
};
|
||||||
|
readonly __typename: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
readonly " $refType": "ExchangeHistory_buyCryptoOrders";
|
||||||
|
};
|
||||||
|
export type ExchangeHistory_buyCryptoOrders$data = ExchangeHistory_buyCryptoOrders;
|
||||||
|
export type ExchangeHistory_buyCryptoOrders$key = {
|
||||||
|
readonly " $data"?: ExchangeHistory_buyCryptoOrders$data;
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"ExchangeHistory_buyCryptoOrders">;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const node: ReaderFragment = {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "ExchangeHistory_buyCryptoOrders",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BuyCryptoOrderEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BuyCryptoOrder",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "paidAmountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "receivedAmount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Currency",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "currency",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "BuyCryptoOrderConnection",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
(node as any).hash = 'da0e93160594d0e07defe123a9bd755b';
|
||||||
|
export default node;
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type ProcessStatus = "CANCELED" | "COMPLETED" | "PROCESSING" | "%future added value";
|
||||||
|
export type ExchangeHistory_sellCryptoOrders = {
|
||||||
|
readonly edges: ReadonlyArray<{
|
||||||
|
readonly node: {
|
||||||
|
readonly id: string;
|
||||||
|
readonly status: ProcessStatus;
|
||||||
|
readonly paidAmount: string;
|
||||||
|
readonly receivedAmountCents: number | null;
|
||||||
|
readonly createdAt: unknown;
|
||||||
|
readonly currency: {
|
||||||
|
readonly name: string;
|
||||||
|
};
|
||||||
|
readonly __typename: string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
readonly " $refType": "ExchangeHistory_sellCryptoOrders";
|
||||||
|
};
|
||||||
|
export type ExchangeHistory_sellCryptoOrders$data = ExchangeHistory_sellCryptoOrders;
|
||||||
|
export type ExchangeHistory_sellCryptoOrders$key = {
|
||||||
|
readonly " $data"?: ExchangeHistory_sellCryptoOrders$data;
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"ExchangeHistory_sellCryptoOrders">;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const node: ReaderFragment = {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "ExchangeHistory_sellCryptoOrders",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "SellCryptoOrderEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "SellCryptoOrder",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "paidAmount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "receivedAmountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Currency",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "currency",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "SellCryptoOrderConnection",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
(node as any).hash = '384d1326525240c150d450555796a621';
|
||||||
|
export default node;
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import type { FC } from "react";
|
||||||
|
import React from "react";
|
||||||
|
import cx from "classnames";
|
||||||
|
|
||||||
|
import type { ProcessStatus } from "../__generated__/ExchangeHistory_buyCryptoOrders.graphql";
|
||||||
|
|
||||||
|
export type CryptoExchangeOrderProps = {
|
||||||
|
payed?: string;
|
||||||
|
received?: string;
|
||||||
|
status?: ProcessStatus;
|
||||||
|
createdAt?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusTextAndColors = (status: ProcessStatus) => {
|
||||||
|
if (status === "PROCESSING") {
|
||||||
|
return ["Processando", "text-yellow-900", "bg-yellow-200"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === "CANCELED") {
|
||||||
|
return ["Cancelado", "text-red-900", "bg-red-200"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ["Completado", "text-green-900", "bg-green-200"];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CryptoExchangeOrder: FC<CryptoExchangeOrderProps> = ({
|
||||||
|
createdAt = "",
|
||||||
|
payed = "",
|
||||||
|
received = "",
|
||||||
|
status = "PROCESSING",
|
||||||
|
}) => {
|
||||||
|
const [label, textStyles, bgStyles] = getStatusTextAndColors(status);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
||||||
|
<p className="text-gray-900 whitespace-nowrap">{payed}</p>
|
||||||
|
</td>
|
||||||
|
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
||||||
|
<p className="text-gray-900 whitespace-nowrap">{received}</p>
|
||||||
|
</td>
|
||||||
|
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
||||||
|
<p className="text-gray-900 whitespace-nowrap">{createdAt}</p>
|
||||||
|
</td>
|
||||||
|
<td className="px-5 py-5 border-b border-gray-200 bg-white text-sm">
|
||||||
|
<span
|
||||||
|
className={cx(
|
||||||
|
"relative inline-block px-3 py-1 font-semibold text-red-900 leading-tight",
|
||||||
|
textStyles
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cx("absolute inset-0 opacity-50 rounded-full", bgStyles)}
|
||||||
|
/>
|
||||||
|
<span className="relative">{label}</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
|
||||||
|
import { ReaderFragment } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type ProcessStatus = "CANCELED" | "COMPLETED" | "PROCESSING" | "%future added value";
|
||||||
|
export type SellOrder_order = {
|
||||||
|
readonly id: string;
|
||||||
|
readonly status: ProcessStatus;
|
||||||
|
readonly paidAmount: string;
|
||||||
|
readonly receivedAmountCents: number | null;
|
||||||
|
readonly createdAt: unknown;
|
||||||
|
readonly __typename: "SellCryptoOrder";
|
||||||
|
readonly " $refType": "SellOrder_order";
|
||||||
|
};
|
||||||
|
export type SellOrder_order$data = SellOrder_order;
|
||||||
|
export type SellOrder_order$key = {
|
||||||
|
readonly " $data"?: SellOrder_order$data;
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"SellOrder_order">;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const node: ReaderFragment = {
|
||||||
|
"argumentDefinitions": [],
|
||||||
|
"kind": "Fragment",
|
||||||
|
"metadata": null,
|
||||||
|
"name": "SellOrder_order",
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "id",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "paidAmount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "receivedAmountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "SellCryptoOrder",
|
||||||
|
"abstractKey": null
|
||||||
|
};
|
||||||
|
(node as any).hash = 'f621be85ef9b711e1c720d4fc07187c7';
|
||||||
|
export default node;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./ExchangeHistory";
|
||||||
@@ -3,21 +3,20 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { ConcreteRequest } from "relay-runtime";
|
import { ConcreteRequest } from "relay-runtime";
|
||||||
|
import { FragmentRefs } from "relay-runtime";
|
||||||
export type ExchangeQueryVariables = {};
|
export type ExchangeQueryVariables = {};
|
||||||
export type ExchangeQueryResponse = {
|
export type ExchangeQueryResponse = {
|
||||||
readonly fiatBalances: {
|
readonly fiatBalances: {
|
||||||
readonly edges: ReadonlyArray<{
|
readonly " $fragmentRefs": FragmentRefs<"CreateExchangeOrderModal_fiatBalances">;
|
||||||
readonly node: {
|
|
||||||
readonly amountCents: number;
|
|
||||||
};
|
|
||||||
}>;
|
|
||||||
};
|
};
|
||||||
readonly balances: {
|
readonly balances: {
|
||||||
readonly edges: ReadonlyArray<{
|
readonly " $fragmentRefs": FragmentRefs<"CreateExchangeOrderModal_balances">;
|
||||||
readonly node: {
|
|
||||||
readonly amount: string;
|
|
||||||
};
|
};
|
||||||
}>;
|
readonly buyCryptoOrders: {
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"ExchangeHistory_buyCryptoOrders">;
|
||||||
|
};
|
||||||
|
readonly sellCryptoOrders: {
|
||||||
|
readonly " $fragmentRefs": FragmentRefs<"ExchangeHistory_sellCryptoOrders">;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export type ExchangeQuery = {
|
export type ExchangeQuery = {
|
||||||
@@ -30,6 +29,29 @@ export type ExchangeQuery = {
|
|||||||
/*
|
/*
|
||||||
query ExchangeQuery {
|
query ExchangeQuery {
|
||||||
fiatBalances {
|
fiatBalances {
|
||||||
|
...CreateExchangeOrderModal_fiatBalances
|
||||||
|
}
|
||||||
|
balances {
|
||||||
|
...CreateExchangeOrderModal_balances
|
||||||
|
}
|
||||||
|
buyCryptoOrders {
|
||||||
|
...ExchangeHistory_buyCryptoOrders
|
||||||
|
}
|
||||||
|
sellCryptoOrders {
|
||||||
|
...ExchangeHistory_sellCryptoOrders
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment CreateExchangeOrderModal_balances on BalanceConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
amount
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment CreateExchangeOrderModal_fiatBalances on FiatBalanceConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
amountCents
|
amountCents
|
||||||
@@ -37,12 +59,37 @@ query ExchangeQuery {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
balances {
|
|
||||||
|
fragment ExchangeHistory_buyCryptoOrders on BuyCryptoOrderConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
amount
|
id
|
||||||
|
status
|
||||||
|
createdAt
|
||||||
|
paidAmountCents
|
||||||
|
receivedAmount
|
||||||
|
currency {
|
||||||
|
name
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment ExchangeHistory_sellCryptoOrders on SellCryptoOrderConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
paidAmount
|
||||||
|
receivedAmountCents
|
||||||
|
createdAt
|
||||||
|
currency {
|
||||||
|
name
|
||||||
|
id
|
||||||
|
}
|
||||||
|
__typename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -53,21 +100,47 @@ var v0 = {
|
|||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "amountCents",
|
"name": "id",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v1 = {
|
v1 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "amount",
|
"name": "status",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v2 = {
|
v2 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "id",
|
"name": "createdAt",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v3 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Currency",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "currency",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "name",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v0/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v4 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
@@ -86,27 +159,9 @@ return {
|
|||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
"args": null,
|
||||||
"concreteType": "FiatBalanceEdge",
|
"kind": "FragmentSpread",
|
||||||
"kind": "LinkedField",
|
"name": "CreateExchangeOrderModal_fiatBalances"
|
||||||
"name": "edges",
|
|
||||||
"plural": true,
|
|
||||||
"selections": [
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"concreteType": "FiatBalance",
|
|
||||||
"kind": "LinkedField",
|
|
||||||
"name": "node",
|
|
||||||
"plural": false,
|
|
||||||
"selections": [
|
|
||||||
(v0/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -120,27 +175,41 @@ return {
|
|||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
"args": null,
|
||||||
"concreteType": "BalanceEdge",
|
"kind": "FragmentSpread",
|
||||||
"kind": "LinkedField",
|
"name": "CreateExchangeOrderModal_balances"
|
||||||
"name": "edges",
|
|
||||||
"plural": true,
|
|
||||||
"selections": [
|
|
||||||
{
|
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"concreteType": "Balance",
|
|
||||||
"kind": "LinkedField",
|
|
||||||
"name": "node",
|
|
||||||
"plural": false,
|
|
||||||
"selections": [
|
|
||||||
(v1/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BuyCryptoOrderConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "buyCryptoOrders",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"args": null,
|
||||||
|
"kind": "FragmentSpread",
|
||||||
|
"name": "ExchangeHistory_buyCryptoOrders"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "SellCryptoOrderConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "sellCryptoOrders",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"args": null,
|
||||||
|
"kind": "FragmentSpread",
|
||||||
|
"name": "ExchangeHistory_sellCryptoOrders"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -179,8 +248,14 @@ return {
|
|||||||
"name": "node",
|
"name": "node",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v0/*: any*/),
|
{
|
||||||
(v2/*: any*/)
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v0/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -214,8 +289,118 @@ return {
|
|||||||
"name": "node",
|
"name": "node",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "amount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v0/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BuyCryptoOrderConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "buyCryptoOrders",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BuyCryptoOrderEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "BuyCryptoOrder",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v0/*: any*/),
|
||||||
(v1/*: any*/),
|
(v1/*: any*/),
|
||||||
(v2/*: any*/)
|
(v2/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "paidAmountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "receivedAmount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v3/*: any*/),
|
||||||
|
(v4/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "SellCryptoOrderConnection",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "sellCryptoOrders",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "SellCryptoOrderEdge",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "edges",
|
||||||
|
"plural": true,
|
||||||
|
"selections": [
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "SellCryptoOrder",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "node",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v0/*: any*/),
|
||||||
|
(v1/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "paidAmount",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "receivedAmountCents",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v2/*: any*/),
|
||||||
|
(v3/*: any*/),
|
||||||
|
(v4/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -228,14 +413,14 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "bb1b8283beba2daf38bacec716816383",
|
"cacheID": "ddb6670ea93a9fdc62c7627c3ed09925",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "ExchangeQuery",
|
"name": "ExchangeQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query ExchangeQuery {\n fiatBalances {\n edges {\n node {\n amountCents\n id\n }\n }\n }\n balances {\n edges {\n node {\n amount\n id\n }\n }\n }\n}\n"
|
"text": "query ExchangeQuery {\n fiatBalances {\n ...CreateExchangeOrderModal_fiatBalances\n }\n balances {\n ...CreateExchangeOrderModal_balances\n }\n buyCryptoOrders {\n ...ExchangeHistory_buyCryptoOrders\n }\n sellCryptoOrders {\n ...ExchangeHistory_sellCryptoOrders\n }\n}\n\nfragment CreateExchangeOrderModal_balances on BalanceConnection {\n edges {\n node {\n amount\n id\n }\n }\n}\n\nfragment CreateExchangeOrderModal_fiatBalances on FiatBalanceConnection {\n edges {\n node {\n amountCents\n id\n }\n }\n}\n\nfragment ExchangeHistory_buyCryptoOrders on BuyCryptoOrderConnection {\n edges {\n node {\n id\n status\n createdAt\n paidAmountCents\n receivedAmount\n currency {\n name\n id\n }\n __typename\n }\n }\n}\n\nfragment ExchangeHistory_sellCryptoOrders on SellCryptoOrderConnection {\n edges {\n node {\n id\n status\n paidAmount\n receivedAmountCents\n createdAt\n currency {\n name\n id\n }\n __typename\n }\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
(node as any).hash = '517d3bf7bc6330021f8eb615e78417f5';
|
(node as any).hash = 'cc0eaddc68f5bd14d39ce9e148876535';
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -2,12 +2,19 @@ import BigNumber from "bignumber.js";
|
|||||||
|
|
||||||
import { BLOCKS_PER_YEAR } from "../constants";
|
import { BLOCKS_PER_YEAR } from "../constants";
|
||||||
|
|
||||||
export const getApr = (
|
type Props = {
|
||||||
stakingTokenPrice: number,
|
stakingTokenPrice: number;
|
||||||
rewardTokenPrice: number,
|
rewardTokenPrice: number;
|
||||||
totalStaked: number,
|
totalStaked: number;
|
||||||
tokenPerBlock: number
|
tokenPerBlock: number;
|
||||||
) => {
|
};
|
||||||
|
|
||||||
|
export const getApr = ({
|
||||||
|
rewardTokenPrice,
|
||||||
|
stakingTokenPrice,
|
||||||
|
tokenPerBlock,
|
||||||
|
totalStaked,
|
||||||
|
}: Props) => {
|
||||||
const totalRewardPricePerYear = new BigNumber(rewardTokenPrice)
|
const totalRewardPricePerYear = new BigNumber(rewardTokenPrice)
|
||||||
.times(tokenPerBlock)
|
.times(tokenPerBlock)
|
||||||
.times(BLOCKS_PER_YEAR);
|
.times(BLOCKS_PER_YEAR);
|
||||||
|
|||||||
5
app/javascript/src/utils/fiatMoney.ts
Normal file
5
app/javascript/src/utils/fiatMoney.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export const centsToUnit = (value?: number | null) => {
|
||||||
|
if (!value) return "0.00";
|
||||||
|
|
||||||
|
return (value / 100).toFixed(2);
|
||||||
|
};
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"bignumber.js": "^9.0.1",
|
"bignumber.js": "^9.0.1",
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"ethers": "^5.4.4",
|
"ethers": "^5.4.4",
|
||||||
|
"graphql-scalars": "^1.10.0",
|
||||||
"postcss": "^7",
|
"postcss": "^7",
|
||||||
"ramda": "^0.27.1",
|
"ramda": "^0.27.1",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
|
|||||||
12
yarn.lock
generated
12
yarn.lock
generated
@@ -4673,6 +4673,13 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
|
|||||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
|
||||||
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
|
||||||
|
|
||||||
|
graphql-scalars@^1.10.0:
|
||||||
|
version "1.10.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/graphql-scalars/-/graphql-scalars-1.10.0.tgz#9daf9252b16e6fae553a06976163a23f41b65dfd"
|
||||||
|
integrity sha512-LONlj8FfhA2iGpkZJWf5e4PVAHXxnZEHSOEvowLYvNXl/TNnhIck8VmE+lren/aa6GKrG+lZufo5lgnyjxcF6g==
|
||||||
|
dependencies:
|
||||||
|
tslib "~2.2.0"
|
||||||
|
|
||||||
"graphql@^14.0.0 || ^15.0.0", graphql@^15.5.1:
|
"graphql@^14.0.0 || ^15.0.0", graphql@^15.5.1:
|
||||||
version "15.5.1"
|
version "15.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"
|
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"
|
||||||
@@ -8912,6 +8919,11 @@ tslib@^1.8.1:
|
|||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||||
|
|
||||||
|
tslib@~2.2.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
|
||||||
|
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
|
||||||
|
|
||||||
tsutils@^3.21.0:
|
tsutils@^3.21.0:
|
||||||
version "3.21.0"
|
version "3.21.0"
|
||||||
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
|
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
|
||||||
|
|||||||
Reference in New Issue
Block a user