Skip to content

Commit

Permalink
feat: Update dependencies, improve swap estimation, and refactor proj…
Browse files Browse the repository at this point in the history
…ect structure

- Updated `react` and `react-dom` to version 18.3.1.
- Added `react-tooltip` as a new dependency for enhanced tooltips.
- Moved `fullPoolsData` to the appropriate components directory for better project organization.
- Removed obsolete files (`doc.html`, `fullPoolsData.ts`) and cleaned up commented-out code.
- Implemented a refactor in the SwapResult component to apply tooltips consistently across labels and icons.
- Enhanced the gas cost tooltip to dynamically explain single-hop and multi-hop fees to improve user clarity.

These changes enhance the code structure, ensure up-to-date dependencies, and improve user experience with more informative tooltips.
  • Loading branch information
dredshep committed Aug 19, 2024
1 parent bbd36af commit f7e0c7f
Show file tree
Hide file tree
Showing 11 changed files with 922 additions and 36 deletions.
142 changes: 142 additions & 0 deletions components/app/Testing/SelectComponent2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import React, { useState, useEffect } from "react";
import fullPoolsData from "@/outputs/fullPoolsData.json";
import { fetchTokenData, getTokenName } from "@/utils/apis/tokenInfo";

interface SelectComponentProps {
apiUrl?: string;
setFrom?: (from: string) => void;
setTo?: (to: string) => void;
outputOptions?: string[]; // New prop for filtered output options
}

const SelectComponent2: React.FC<SelectComponentProps> = ({
apiUrl = "http://localhost:3000/api/tokens",
setFrom,
setTo,
outputOptions = [], // Default to empty array if not provided
}) => {
const [fromTokens, setFromTokens] = useState<string[]>([]);
const [toTokens, setToTokens] = useState<string[]>([]);
const [selectedFrom, setSelectedFrom] = useState<string>("");
const [selectedTo, setSelectedTo] = useState<string>("");
const [tokenNames, setTokenNames] = useState<{ [key: string]: string }>({});

useEffect(() => {
if (setFrom) {
setFrom(selectedFrom);
}
if (setTo) {
setTo(selectedTo);
}
}, [selectedFrom, selectedTo, setFrom, setTo]);

useEffect(() => {
const fetchAndSetTokenData = async () => {
await fetchTokenData(apiUrl);

const names = fullPoolsData
.flatMap((pool) => pool.query_result.assets)
.map((asset) => {
const address =
asset.info.token?.contract_addr || asset.info.native_token?.denom;
return address ? [address, getTokenName(address) || ""] : null;
})
.filter((item): item is [string, string] => item !== null)
.reduce((acc, [address, name]) => {
acc[address] = name;
return acc;
}, {} as { [key: string]: string });

setTokenNames(names);

const fromOptions = fullPoolsData
.flatMap((pool) => pool.query_result.assets)
.map(
(asset) =>
asset.info.token?.contract_addr || asset.info.native_token?.denom
)
.filter(
(addressOrDenom): addressOrDenom is string =>
addressOrDenom !== undefined
)
.filter((address) => address !== "uscrt");

setFromTokens(Array.from(new Set(fromOptions)));
};

fetchAndSetTokenData();
}, [apiUrl]);

const handleFromSelect = (event: React.ChangeEvent<HTMLSelectElement>) => {
const fromToken = event.target.value;
setSelectedFrom(fromToken);

if (outputOptions.length > 0) {
setToTokens(outputOptions);
} else {
const toOptions = fullPoolsData
.filter((pool) =>
pool.query_result.assets.some(
(asset) =>
asset.info.token?.contract_addr === fromToken ||
asset.info.native_token?.denom === fromToken
)
)
.flatMap((pool) =>
pool.query_result.assets.map((asset) => {
const addr =
asset.info.token?.contract_addr || asset.info.native_token?.denom;
return addr && addr !== fromToken && tokenNames[addr] ? addr : null;
})
)
.filter((addr): addr is string => addr !== null)
.filter((addr) => addr !== "uscrt");

setToTokens(Array.from(new Set(toOptions)));
}

setSelectedTo(""); // Reset the selected 'to' token
};

const handleToSelect = (event: React.ChangeEvent<HTMLSelectElement>) => {
setSelectedTo(event.target.value);
};

return (
<div className="flex flex-col space-y-4">
<div className="flex flex-col space-y-2">
<label className="text-white">Select From</label>
<select
value={selectedFrom}
onChange={handleFromSelect}
className="px-4 py-2 border border-gray-700 rounded-lg focus:outline-none focus:ring-2 bg-adamant-app-selectTrigger focus:ring-adamant-accentBg text-white"
>
<option value="">Select From</option>
{fromTokens.map((address) => (
<option key={address} value={address}>
{tokenNames[address] ?? JSON.stringify(address)}
</option>
))}
</select>
</div>
<div className="flex flex-col space-y-2">
<label className="text-white">Select To</label>
<select
disabled={!selectedFrom}
value={selectedTo}
onChange={handleToSelect}
className="px-4 py-2 border border-gray-700 rounded-lg focus:outline-none focus:ring-2 bg-adamant-app-selectTrigger focus:ring-adamant-accentBg text-white"
>
<option value="">Select To</option>
{toTokens.map((address) => (
<option key={address} value={address}>
{tokenNames[address] ?? JSON.stringify(address)}
</option>
))}
</select>
</div>
</div>
);
};

export default SelectComponent2;
170 changes: 170 additions & 0 deletions components/app/Testing/SwapResult.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React from "react";
import { FiCheckCircle, FiAlertTriangle, FiAlertOctagon } from "react-icons/fi";
import { Tooltip } from "react-tooltip";
import "react-tooltip/dist/react-tooltip.css";

// Props for each SwapResultItem
interface SwapResultItemProps {
label: string;
value: string;
tooltipId: string;
tooltipContent: string;
valueClassName?: string;
icon?: JSX.Element;
}

// SwapResultItem functional component
const SwapResultItem: React.FC<SwapResultItemProps> = ({
label,
value,
tooltipId,
tooltipContent,
valueClassName,
icon,
}) => {
return (
<div className="flex justify-between items-center gap-8">
<span
className="text-md font-semibold text-gray-400 border-b leading-[18px] border-gray-600 hover:border-gray-500 cursor-help"
data-tooltip-id={tooltipId}
>
{label}:
</span>
<div className="flex items-center">
<span
className={`text-lg font-bold ${valueClassName}`}
// data-tooltip-id={tooltipId} // Apply the tooltip to the value text
>
{value}
</span>
{icon && React.cloneElement(icon, { "data-tooltip-id": tooltipId })}{" "}
{/* Apply the tooltip to the icon */}
</div>
<Tooltip id={tooltipId} place="top" content={tooltipContent} />
</div>
);
};

// Main SwapResult component
interface SwapResultProps {
bestRoute: string;
idealOutput: string;
actualOutput: string;
priceImpact: string;
lpFee: string;
gasCost: string;
isMultiHop: boolean;
difference: string;
}

const SwapResult: React.FC<SwapResultProps> = ({
bestRoute,
idealOutput,
actualOutput,
priceImpact,
lpFee,
gasCost,
isMultiHop,
difference,
}) => {
// Array to store the data and config for each section
const data = [
{
label: "Best Route",
value: bestRoute,
tooltipId: "bestRouteTip",
tooltipContent:
"The optimal route for swapping your tokens to minimize slippage and maximize output.",
valueClassName: "text-blue-400",
},
{
label: "Ideal Output",
value: idealOutput,
tooltipId: "idealOutputTip",
tooltipContent:
"The best possible output you could get without any fees or slippage.",
valueClassName: "text-blue-400",
},
{
label: "Actual Output",
value: actualOutput,
tooltipId: "actualOutputTip",
tooltipContent:
"The actual amount of tokens you received after fees and slippage.",
valueClassName: "text-green-400",
},
{
label: "Difference",
value: difference,
tooltipId: "differenceTip",
tooltipContent:
"The difference between the ideal and actual output, caused by fees and slippage.",
valueClassName: "text-red-400",
},
{
label: "Price Impact",
value: priceImpact,
tooltipId: "priceImpactTip",
tooltipContent:
"The effect your trade has on the market price of the token pair.",
valueClassName: priceImpactColor(priceImpact),
icon: priceImpactIcon(priceImpact),
},
{
label: "LP Fee",
value: lpFee,
tooltipId: "lpFeeTip",
tooltipContent:
"The fee paid to liquidity providers for facilitating your trade.",
valueClassName: "text-yellow-300",
},
{
label: "Gas Cost",
value: gasCost,
tooltipId: "gasCostTip",
tooltipContent: isMultiHop
? "This gas cost includes multiple hops. Each hop incurs a separate gas fee. The total gas cost is the sum of all hops."
: "This gas cost covers a single swap between two tokens in one liquidity pool.",
valueClassName: "text-gray-300",
icon: <FiCheckCircle className="ml-2 w-5 h-5 cursor-help" />, // Removed tooltipId here, will be added dynamically
},
];

return (
<div className="p-6 bg-gray-800 rounded-lg shadow-md text-white space-y-4 max-w-md mx-auto">
<div className="text-center">
<h2 className="text-4xl font-bold text-green-400">Swap Estimate</h2>
</div>
<div className="flex flex-col space-y-2">
{data.map((item, index) => (
<SwapResultItem
key={index}
label={item.label}
value={item.value}
tooltipId={item.tooltipId}
tooltipContent={item.tooltipContent}
valueClassName={item.valueClassName}
icon={item.icon}
/>
))}
</div>
</div>
);
};

// Utility functions for dynamic styling
const priceImpactColor = (priceImpact: string) => {
const impact = parseFloat(priceImpact);
if (impact < 1) return "text-green-400";
if (impact < 3) return "text-yellow-400";
return "text-red-400";
};

const priceImpactIcon = (priceImpact: string) => {
const impact = parseFloat(priceImpact);
if (impact < 1.1) return <FiCheckCircle className="ml-2 w-5 h-5" />;
if (impact < 3) return <FiAlertTriangle className="ml-2 w-5 h-5" />;
return <FiAlertOctagon className="ml-2 w-5 h-5" />;
};

export default SwapResult;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@
"marked": "^12.0.2",
"moment": "^2.30.1",
"next": "14.1.3",
"react": "18.2",
"react-dom": "18.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.51.5",
"react-icons": "^5.2.1",
"react-toastify": "^10.0.5",
"react-tooltip": "^5.28.0",
"react-vega": "^7.6.0",
"secretjs": "^1.12.5",
"seedrandom": "^3.0.5",
Expand All @@ -68,6 +69,6 @@
"eslint-config-next": "14.1.3",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.4",
"typescript": "^5.4.5"
"typescript": "^5.5.4"
}
}
2 changes: 1 addition & 1 deletion pages/app/testing/decimals/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { SecretNetworkClient } from "secretjs";
import { Window as KeplrWindow } from "@keplr-wallet/types";
import { processPoolsData } from "../../../../utils/secretjs/decimals/utils/processPoolData";
import { fullPoolsData } from "../fullPoolsData";
import { fullPoolsData } from "../../../../components/app/Testing/fullPoolsData";

const QueryTokenDecimals = () => {
const [secretjs, setSecretjs] = useState<SecretNetworkClient | null>(null);
Expand Down
Loading

0 comments on commit f7e0c7f

Please sign in to comment.