Skip to content

Commit a3df646

Browse files
Add best smart router (#562)
* add best smart router * add router * add smartRouter testnet env * update slippage value * add price impack logic for no price tokens * add exception logic * fix ui issue * fix issues * add test data * add user wallet record * remove test data * add application header * add test data * remove test data * ui optimization * update smartRouter api domain
1 parent 655919e commit a3df646

File tree

14 files changed

+991
-395
lines changed

14 files changed

+991
-395
lines changed

src/components/layout/SwapRoutes.tsx

Lines changed: 166 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import { toRealSymbol } from '../../utils/token';
1515

1616
import { EstimateSwapView } from 'src/services/swap';
1717

18-
import { getPoolAllocationPercents, percent } from '../../utils/numbers';
18+
import {
19+
getPoolAllocationPercents,
20+
percent,
21+
getRouteAllocationPercents,
22+
} from '../../utils/numbers';
1923
import { Pool } from '../../services/pool';
2024
import { FaAngleUp, FaAngleDown } from '../reactIcons';
2125
import { Card } from '../card/Card';
@@ -72,8 +76,14 @@ import { displayNumberToAppropriateDecimals } from '../../services/commonV3';
7276
import { numberWithCommas } from '../../pages/Orderly/utiles';
7377
import { get_pool_name, openUrl } from '../../services/commonV3';
7478
import getConfigV2 from '../../services/configV2';
79+
7580
import { REF_FI_BEST_MARKET_ROUTE } from '../../state/swap';
7681
import { PolygonRight } from '../../pages/Orderly/components/Common/Icons';
82+
import {
83+
IEstimateSwapServerView,
84+
IServerRoute,
85+
getTokensOfRoute,
86+
} from '../../services/smartRouterFromServer';
7787
const configV2 = getConfigV2();
7888
export const GetPriceImpact = (
7989
value: string,
@@ -717,16 +727,23 @@ export const getDexAction = (market: SwapMarket) => {
717727

718728
export const SwapRoute = ({
719729
route,
720-
p,
721730
market,
731+
routeServer,
722732
}: {
723-
route: EstimateSwapView[];
724-
p: string;
725-
tokenIn: TokenMetadata;
726-
tokenOut: TokenMetadata;
733+
route?: EstimateSwapView[];
727734
market: SwapMarket;
735+
routeServer?: IServerRoute;
728736
}) => {
729-
const tokens = route[0].tokens;
737+
const [tokens, setTokens] = useState([]);
738+
useEffect(() => {
739+
if (route) {
740+
setTokens(route[0].tokens);
741+
} else if (routeServer) {
742+
getTokensOfRoute(routeServer).then((res) => {
743+
setTokens(res);
744+
});
745+
}
746+
}, [route, routeServer]);
730747

731748
const { swapType } = useContext(SwapProContext);
732749

@@ -771,6 +788,7 @@ export const SwapRoute = ({
771788
tokens.map((t, i) => {
772789
return (
773790
<div
791+
key={i}
774792
className={`text-xs ${
775793
tokens.length === 3 && toRealSymbol(t.symbol).length > 7
776794
? 'xsm:overflow-hidden'
@@ -802,9 +820,11 @@ export const SwapRoute = ({
802820
export const SwapRouteMoreThan2 = ({
803821
market,
804822
trade,
823+
throughPools,
805824
}: {
806825
market: SwapMarket;
807826
trade: ExchangeEstimate;
827+
throughPools: number;
808828
}) => {
809829
const intl = useIntl();
810830

@@ -846,7 +866,7 @@ export const SwapRouteMoreThan2 = ({
846866
id: 'steps_in_the_route_zh',
847867
})}
848868
<span className={intl.locale === 'zh-CN' ? 'mr-0' : 'mr-1'}>
849-
{trade.estimates.length}
869+
{throughPools}
850870
</span>
851871

852872
<FormattedMessage
@@ -1691,7 +1711,7 @@ export const TradeRouteHub = ({
16911711
>
16921712
<div className="border-b pb-1 frcs border-primaryText border-opacity-30">
16931713
<DisplayIcon token={token} height="14px" width="14px" />
1694-
<span className="ml-1 text-white text-xs w-10 mr-4">
1714+
<span className="block ml-1 text-white text-xs w-28 truncate">
16951715
{toRealSymbol(token.symbol)}
16961716
</span>
16971717
</div>
@@ -1742,7 +1762,7 @@ export const RightBracket = ({ size }: { size: number }) => {
17421762
<div
17431763
className="w-4 mr-3 opacity-30 rounded-full relative z-10 border border-primaryText "
17441764
style={{
1745-
height: `${size * 35}px`,
1765+
height: `${size * 60}px`,
17461766
clipPath: `polygon(50% 0, 100% 0,100% 100%, 50% 100%)`,
17471767
}}
17481768
></div>
@@ -1754,7 +1774,7 @@ export const LeftBracket = ({ size }: { size: number }) => {
17541774
<div
17551775
className="w-4 ml-3 opacity-30 rounded-full relative z-10 border border-primaryText transform rotate-180"
17561776
style={{
1757-
height: `${size * 35}px`,
1777+
height: `${size * 60}px`,
17581778
clipPath: `polygon(50% 0, 100% 0,100% 100%, 50% 100%)`,
17591779
}}
17601780
></div>
@@ -1770,6 +1790,47 @@ export const TradeRoute = ({
17701790
tokenIn: TokenMetadata;
17711791
tokenOut: TokenMetadata;
17721792
}) => {
1793+
const [estimatesServerUI, setEstimatesServerUI] =
1794+
useState<IEstimateSwapServerView>();
1795+
const { estimates, estimatesServer } = trade || {};
1796+
const identicalRoutes = estimates
1797+
? separateRoutes(estimates, estimates[estimates.length - 1].outputToken)
1798+
: [];
1799+
1800+
const pools = identicalRoutes.map((r) => r[0]).map((hub) => hub.pool);
1801+
useEffect(() => {
1802+
if (estimatesServer) {
1803+
getTokensDataOfEstimatesServer();
1804+
} else {
1805+
setEstimatesServerUI(undefined);
1806+
}
1807+
}, [JSON.stringify(estimatesServer || {})]);
1808+
async function getTokensDataOfEstimatesServer() {
1809+
const pending = estimatesServer.routes.map((route) =>
1810+
getTokensOfRoute(route)
1811+
);
1812+
const tokens_of_routes = await Promise.all(pending);
1813+
estimatesServer.routes.map((route, index) => {
1814+
route.tokens = tokens_of_routes[index];
1815+
});
1816+
setEstimatesServerUI(estimatesServer);
1817+
}
1818+
const percents = useMemo(() => {
1819+
try {
1820+
return getPoolAllocationPercents(pools);
1821+
} catch (error) {
1822+
if (identicalRoutes.length === 0) return ['100'];
1823+
else return identicalRoutes.map((r) => r[0].percent);
1824+
}
1825+
}, [identicalRoutes, pools]);
1826+
const percentsServer = useMemo(() => {
1827+
const routes = estimatesServer?.routes || [];
1828+
try {
1829+
return getRouteAllocationPercents(routes);
1830+
} catch (error) {
1831+
return ['100'];
1832+
}
1833+
}, [(estimatesServer?.routes || []).length]);
17731834
if (!tokenIn || !tokenOut) return null;
17741835

17751836
if (!trade || !trade?.availableRoute || tokenIn?.id === tokenOut?.id) {
@@ -1801,85 +1862,119 @@ export const TradeRoute = ({
18011862
</div>
18021863
);
18031864
}
1804-
1805-
const { estimates } = trade;
1806-
1807-
const { market } = trade;
1808-
1809-
const identicalRoutes = separateRoutes(
1810-
estimates,
1811-
estimates[estimates.length - 1].outputToken
1812-
);
1813-
1814-
const pools = identicalRoutes.map((r) => r[0]).map((hub) => hub.pool);
1815-
1816-
const percents = useMemo(() => {
1817-
try {
1818-
return getPoolAllocationPercents(pools);
1819-
} catch (error) {
1820-
if (identicalRoutes.length === 0) return ['100'];
1821-
else return identicalRoutes.map((r) => r[0].percent);
1822-
}
1823-
}, [identicalRoutes, pools]);
1824-
1865+
const routeLength =
1866+
identicalRoutes.length || trade.estimatesServer?.routes?.length;
18251867
return (
18261868
<div className="frcb">
18271869
<DisplayIcon token={tokenIn} height="26px" width="26px" />
1828-
<LeftBracket size={identicalRoutes.length} />
1829-
<div className="w-full mx-2 xsm:overflow-x-auto hideScroll relative">
1830-
{identicalRoutes.map((route, j) => {
1831-
return (
1832-
<div
1833-
className="relative frcb my-3 "
1834-
style={{
1835-
width: isMobile() ? '460px' : '',
1836-
}}
1837-
>
1838-
<span className="text-xs text-senderHot">{percents[j]}%</span>
1870+
<LeftBracket size={routeLength} />
1871+
{/* from script routes */}
1872+
{trade.estimates ? (
1873+
<div className={`w-full mx-2 xsm:overflow-x-auto hideScroll relative`}>
1874+
{identicalRoutes.map((route, j) => {
1875+
return (
18391876
<div
1840-
className="border border-dashed absolute left-5 opacity-30 border-primaryText w-full px-3"
1877+
key={j}
1878+
className="relative frcb my-3 "
18411879
style={{
1842-
width: 'calc(100% - 32px)',
1880+
width: isMobile() ? '460px' : '',
18431881
}}
1844-
></div>
1845-
<div className="frcs">
1846-
{route[0].tokens
1847-
.slice(1, route[0].tokens.length)
1848-
.map((t, i) => {
1882+
>
1883+
<span className="text-xs text-senderHot">{percents[j]}%</span>
1884+
<div
1885+
className="border border-dashed absolute left-5 opacity-30 border-primaryText w-full px-3"
1886+
style={{
1887+
width: 'calc(100% - 32px)',
1888+
}}
1889+
></div>
1890+
<div className="frcs">
1891+
{route[0].tokens
1892+
.slice(1, route[0].tokens.length)
1893+
.map((t, i) => {
1894+
return (
1895+
<>
1896+
<TradeRouteHub
1897+
poolId={
1898+
route[i].contract === 'Ref_DCL'
1899+
? getV3PoolId(
1900+
tokenIn.id,
1901+
tokenOut.id,
1902+
trade.fee * 100
1903+
)
1904+
: route[i].contract === 'Ref_Classic'
1905+
? Number(route[i].pool.id)
1906+
: null
1907+
}
1908+
token={t}
1909+
contract={route[i].contract}
1910+
/>
1911+
{t.id !==
1912+
route[0].tokens[route[0].tokens.length - 1]?.id && (
1913+
<div className="mx-3">
1914+
<PolygonArrow />
1915+
</div>
1916+
)}
1917+
</>
1918+
);
1919+
})}
1920+
</div>
1921+
1922+
<PolygonArrow />
1923+
</div>
1924+
);
1925+
})}
1926+
</div>
1927+
) : null}
1928+
1929+
{/* from server routes */}
1930+
{estimatesServerUI ? (
1931+
<div className={`w-full mx-2 xsm:overflow-x-auto hideScroll relative`}>
1932+
{estimatesServerUI.routes.map((route, j) => {
1933+
const { pools, tokens } = route;
1934+
return (
1935+
<div
1936+
key={j}
1937+
className="relative frcb my-3 "
1938+
style={{
1939+
width: isMobile() ? '620px' : '',
1940+
}}
1941+
>
1942+
<span className="text-xs text-senderHot">
1943+
{percentsServer[j]}%
1944+
</span>
1945+
<div
1946+
className="border border-dashed absolute left-5 opacity-30 border-primaryText w-full px-3"
1947+
style={{
1948+
width: 'calc(100% - 32px)',
1949+
}}
1950+
></div>
1951+
<div className="frcs">
1952+
{tokens?.slice(1, tokens.length).map((t, i) => {
18491953
return (
18501954
<>
18511955
<TradeRouteHub
1852-
poolId={
1853-
route[i].contract === 'Ref_DCL'
1854-
? getV3PoolId(
1855-
tokenIn.id,
1856-
tokenOut.id,
1857-
trade.fee * 100
1858-
)
1859-
: route[i].contract === 'Ref_Classic'
1860-
? Number(route[i].pool.id)
1861-
: null
1862-
}
1956+
poolId={Number(pools[i].pool_id)}
18631957
token={t}
1864-
contract={route[i].contract}
1958+
contract="Ref_Classic"
18651959
/>
1866-
{t.id !==
1867-
route[0].tokens[route[0].tokens.length - 1]?.id && (
1960+
{t.id !== tokens[tokens.length - 1]?.id && (
18681961
<div className="mx-3">
18691962
<PolygonArrow />
18701963
</div>
18711964
)}
18721965
</>
18731966
);
18741967
})}
1968+
</div>
1969+
1970+
<PolygonArrow />
18751971
</div>
1972+
);
1973+
})}
1974+
</div>
1975+
) : null}
18761976

1877-
<PolygonArrow />
1878-
</div>
1879-
);
1880-
})}
1881-
</div>
1882-
<RightBracket size={identicalRoutes.length} />
1977+
<RightBracket size={routeLength} />
18831978

18841979
<DisplayIcon token={tokenOut} height="26px" width="26px" />
18851980
</div>
@@ -1900,7 +1995,7 @@ export const TradeRouteModal = (
19001995
/>
19011996
}
19021997
{...props}
1903-
customWidth={isMobile() ? '95vw' : '700px'}
1998+
customWidth={isMobile() ? '95vw' : '800px'}
19041999
>
19052000
<div className="w-full mt-7">
19062001
<TradeRoute

0 commit comments

Comments
 (0)