By exchange ETH to wstETH you will incrase your ETH holdings balance by {baseAPRstETH.toFixed(2)}% APY from staking liquidity on Lido finance.
diff --git a/src/components/FAQ.tsx b/src/components/FAQ.tsx
index cfe09714..6654f150 100644
--- a/src/components/FAQ.tsx
+++ b/src/components/FAQ.tsx
@@ -61,7 +61,7 @@ export const FAQ: React.FC = () => {
},
{
q: "What are the fees for swapping?",
- a: "Hexa Lite does not charge any fees for swapping. However, the protocols that we integrate with may charge a fee for swapping and on-chain gas fees are applicable according to each network. That's why you have to get some ETH to pay for gas fees on Ethereum network and majors EVM network.",
+ a: "Hexa Lite charge 1% fees for swapping. However, the protocols that we integrate with may charge a fee for swapping and on-chain gas fees are applicable according to each network. That's why you have to get some ETH to pay for gas fees on Ethereum network and majors EVM network.",
},
{
q: "How pay on-chain transaction fees?",
diff --git a/src/components/FooterComponent.tsx b/src/components/FooterComponent.tsx
index 8f9fcb65..4ede242f 100644
--- a/src/components/FooterComponent.tsx
+++ b/src/components/FooterComponent.tsx
@@ -4,12 +4,15 @@ import { logoGithub } from "ionicons/icons";
export const FooterComponent: React.FC = () => {
return (
-
-
+
+
Open source software by{" "}
{
>
@@ -55,10 +58,10 @@ export const FooterComponent: React.FC = () => {
>
@@ -69,9 +72,7 @@ export const FooterComponent: React.FC = () => {
rel="noreferrer noopener"
>
diff --git a/src/components/Header.tsx b/src/components/Header.tsx
index 276de2a0..acf6818a 100644
--- a/src/components/Header.tsx
+++ b/src/components/Header.tsx
@@ -1,17 +1,20 @@
import {
IonButton,
+ IonButtons,
IonCol,
IonGrid,
IonHeader,
IonIcon,
- IonImg,
IonMenuToggle,
+ IonModal,
IonPopover,
IonRow,
IonSegment,
IonSegmentButton,
IonText,
IonToolbar,
+ isPlatform,
+ useIonRouter,
} from "@ionic/react";
import {
ellipsisVerticalSharp,
@@ -26,122 +29,166 @@ import { PointsPopover } from "./PointsPopover";
import { useRef } from "react";
import { getWeb3State } from "@/store/selectors";
import Store from "@/store";
-
-const styleLogo = {
- // margin: '15px auto 20px',
- padding: " 0px",
- width: "42px",
- maxWidth: "42px",
- height: "42px",
- cursor: "pointer",
-};
-
-const styleChip = {
- position: "absolute",
- bottom: "0rem",
- right: "-15px",
- transform: "scale(0.6)",
- padding: " 0rem 0.5rem",
- margin: 0,
- "--color": "var(--ion-color-primary)",
- "--background": "var(--ion-color-warning)",
-};
+import { InstallPWASteps } from "./ui/InstallPWASteps";
+import { AppLogo } from "./ui/AppLogo";
export function Header({
currentSegment,
- scrollToTop,
+ // scrollToTop,
handleSegmentChange,
}: {
currentSegment: string;
- scrollToTop: () => void;
+ // scrollToTop: () => void;
handleSegmentChange: (e: { detail: { value: string } }) => void;
}) {
// define states
const { walletAddress } = Store.useState(getWeb3State);
const [points, setPoints] = useState(null);
const [isPointsPopoverOpen, setIsPointsPopoverOpen] = useState(false);
+ const [isInstallModalOpen, setIsInstallModalOpen] = useState(false);
const pointsPopoverRef = useRef(null);
+ const router = useIonRouter();
const openPopover = (e: any) => {
pointsPopoverRef.current!.event = e;
setIsPointsPopoverOpen(true);
};
useEffect(() => {
- scrollToTop();
+ // scrollToTop();
}, [currentSegment]);
// render component
return (
-
-
-
-
- {!currentSegment || currentSegment === "welcome" ? (
- <>>
- ) : (
- <>
-
-
- handleSegmentChange({ detail: { value: "welcome" } })
- }
+ <>
+
+
+
+
+ {!currentSegment || currentSegment === "welcome" ? (
+ <>{currentSegment}>
+ ) : (
+ <>
+
+ {
+ router.push('/index', 'back');
+ }}
+ >
+
+
+
+
-
-
-
-
- {
- if (e.detail.value === 'fiat-segment') {
- handleSegmentChange({detail: {value: 'fiat'}});
- return;
- };
- handleSegmentChange(e);
- }}
+ {
+ if (e.detail.value === "fiat-segment") {
+ handleSegmentChange({ detail: { value: "fiat" } });
+ return;
+ }
+ router.push(`/${e.detail.value}`);
+ handleSegmentChange(e);
+ }}
+ >
+ Wallet
+ Exchange
+
+ Earn interest
+
+
+ Lend & borrow
+
+
+ Buy
+
+
+
+
- Exchange
-
- Earn Interest
-
-
- Lending & Borrow
-
-
- Buy
-
-
-
-
- {walletAddress ? (
- <>
-
+ {walletAddress ? (
+ <>
+
+
openPopover(e)}
+ >
+
+
+ Points
+
+
+
+
+ Connected
+
+
+
+
+
{
+ setPoints(() => null);
+ setIsPointsPopoverOpen(false);
+ }}
+ onWillPresent={async () => {
+ const response = await getAddressPoints(
+ walletAddress
+ ).catch((error) => {});
+ console.log("response", response);
+ if (response?.data?.totalPoints) {
+ setPoints(() => response.data.totalPoints);
+ } else {
+ setPoints(() => "0");
+ }
+ }}
+ >
+ setIsPointsPopoverOpen(false)}
+ />
+
+
+ >
+ ) : (
+
openPopover(e)}
>
-
-
- Connected
-
-
-
-
-
{
- setPoints(() => null);
- setIsPointsPopoverOpen(false);
- }}
- onWillPresent={async () => {
- const response = await getAddressPoints(
- walletAddress
- ).catch((error) => {});
- console.log("response", response);
- if (response?.data?.totalPoints) {
- setPoints(() => response.data.totalPoints);
- } else {
- setPoints(() => "0");
- }
- }}
- >
- setIsPointsPopoverOpen(false)}
- />
-
+
- >
- ) : (
-
+ )}
+
+ {/* Mobile nav button */}
+
+
-
-
- Points
-
+
-
-
- )}
-
- {/* Mobile nav button */}
-
-
-
-
-
-
-
- >
- )}
-
-
-
-
+
+
+ >
+ )}
+
+
+ {!isPlatform("pwa") && isPlatform("mobile") && (
+
+ {
+ setIsInstallModalOpen(true);
+ }}>
+
+ Install App
+
+
+
+ )}
+
+
+
+
setIsInstallModalOpen(false)}
+ className="modalAlert"
+ >
+
+
+ >
);
}
diff --git a/src/components/LoanFormModal.tsx b/src/components/LoanFormModal.tsx
index 6683bbc5..cb305563 100644
--- a/src/components/LoanFormModal.tsx
+++ b/src/components/LoanFormModal.tsx
@@ -62,6 +62,7 @@ export function LoanFormModal({
const readableAction =
actionType[0].toUpperCase() + actionType.slice(1).toLocaleLowerCase();
+ console.log('>>>', userSummary)
return (
diff --git a/src/components/MATICLiquidStakingstrategy.tsx b/src/components/MATICLiquidStakingstrategy.tsx
index 0a779692..459f237a 100644
--- a/src/components/MATICLiquidStakingstrategy.tsx
+++ b/src/components/MATICLiquidStakingstrategy.tsx
@@ -1,10 +1,13 @@
import {
+ IonAvatar,
IonButton,
+ IonButtons,
IonCard,
IonCardContent,
IonCol,
IonContent,
IonGrid,
+ IonHeader,
IonIcon,
IonImg,
IonItem,
@@ -16,6 +19,8 @@ import {
IonSkeletonText,
IonSpinner,
IonText,
+ IonTitle,
+ IonToolbar,
useIonToast,
} from "@ionic/react";
import { ethers } from "ethers";
@@ -46,7 +51,7 @@ export interface IStrategyModalProps {
) => Promise | undefined;
}
-export function MATICLiquidStakingstrategyCard() {
+export function MATICLiquidStakingstrategyCard(props: { asImage?: boolean, asItem?: boolean }) {
const { web3Provider, switchNetwork, connectWallet, disconnectWallet, currentNetwork } = Store.useState(getWeb3State);
const [baseAPRst, setBaseAPRst] = useState(-1);
const [action, setAction] = useState<"stake" | "unstake">("stake");
@@ -170,7 +175,8 @@ export function MATICLiquidStakingstrategyCard() {
},
hiddenUI: [
...LIFI_CONFIG?.hiddenUI as any,
- HiddenUI.ToAddress
+ HiddenUI.ToAddress,
+ HiddenUI.History,
],
disabledUI: action === 'stake'
? [ "toToken"]
@@ -203,6 +209,7 @@ export function MATICLiquidStakingstrategyCard() {
return (
<>
+ {!props?.asItem && (
@@ -220,11 +227,11 @@ export function MATICLiquidStakingstrategyCard() {
-
+
{strategy.name}
-
- {strategy.type}
+
+ {strategy.type}
@@ -404,6 +411,46 @@ export function MATICLiquidStakingstrategyCard() {
+ )}
+
+ {props?.asItem && !props?.asImage && (
+ {
+ const chainId = currentNetwork;
+ await displayLoader();
+ if (chainId !== NETWORK.optimism) {
+ await switchNetwork(NETWORK.optimism);
+ }
+ await modal.current?.present();
+ await hideLoader();
+ }}>
+
+
+
+
+
+ {strategy.name}
+
+
+
+ {strategy.type}
+
+
+
+
+
+ {strategy.apys[0]}%
+
+
+
+ )}
-
+
+
+
+ {strategy.name}
+ {strategy.type}
+
+
+ {
+ modal.current?.dismiss();
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {strategy.name}
+
+
+ {strategy.type}
+
+
+
+
+
+
-
- {
- modal.current?.dismiss();
- }}
- >
-
-
-
-
-
-
- {strategy.name}
-
-
- {strategy.type}
-
By exchange MATIC to stMATIC you will incrase your MATIC holdings balance by {baseAPRst.toFixed(2)}% APY from staking liquidity on Lido finance.
diff --git a/src/components/MarketsList.tsx b/src/components/MarketsList.tsx
index 0e582adb..f4a5fbb2 100644
--- a/src/components/MarketsList.tsx
+++ b/src/components/MarketsList.tsx
@@ -97,7 +97,14 @@ export function MarketList(props: {
return (
<>
-
+
-
+
{groups.map((poolGroup, index) => (
)}
{groups.length === 0 && totalTVL && (
-
+
diff --git a/src/components/PoolAccordionGroup.tsx b/src/components/PoolAccordionGroup.tsx
index 4e696ad9..5453625d 100644
--- a/src/components/PoolAccordionGroup.tsx
+++ b/src/components/PoolAccordionGroup.tsx
@@ -133,7 +133,7 @@ export function PoolAccordionGroup(props: IPoolAccordionProps) {
size-md="2"
class="ion-text-end ion-hide-sm-down"
>
-
+
{poolGroup.topSupplyApy * 100 === 0
? "0"
: poolGroup.topSupplyApy * 100 < 0.01
@@ -143,7 +143,7 @@ export function PoolAccordionGroup(props: IPoolAccordionProps) {
-
+
{poolGroup?.topBorrowApy * 100 === 0
? poolGroup?.borrowingEnabled === false
? "- "
diff --git a/src/components/PoolItem.tsx b/src/components/PoolItem.tsx
index 7bac987b..3b5021bf 100644
--- a/src/components/PoolItem.tsx
+++ b/src/components/PoolItem.tsx
@@ -1,6 +1,4 @@
import {
- IonAvatar,
- IonBadge,
IonButton,
IonCol,
IonFabButton,
@@ -51,14 +49,15 @@ const ActionBtn = (props: {provider: string}) => {
const { provider } = props;
if (provider === 'aave-v3') {
return (
-
-
-
+
+
)
}
return (
@@ -85,7 +84,6 @@ export function PoolItem(props: IPoolItemProps) {
const { poolId, iconSize, chainId, handleSegmentChange } = props;
const { walletAddress, loadAssets } = Store.useState(getWeb3State);
const poolGroups = Store.useState(getPoolGroupsState);
- const modal = useRef(null);
const [isModalOpen, setIsModalOpen] = useState(false);
// find pool in `poolGroups[*].pool` by `poolId`
const pool = useMemo(() => {
@@ -232,7 +230,6 @@ export function PoolItem(props: IPoolItemProps) {
setIsModalOpen(() => false)}
@@ -240,11 +237,12 @@ export function PoolItem(props: IPoolItemProps) {
{
- modal.current?.dismiss();
+ setIsModalOpen(() => false);
+ // modal.current?.dismiss();
// reload asset if user have trigger an action from ReserveDetails.
// Ex: deposit, withdraw, borrow, repay
if (actionType) {
- loadAssets();
+ loadAssets(true);
}
}}
handleSegmentChange={handleSegmentChange}
diff --git a/src/components/ReserveDetail.tsx b/src/components/ReserveDetail.tsx
index fcee47d9..a37b813b 100644
--- a/src/components/ReserveDetail.tsx
+++ b/src/components/ReserveDetail.tsx
@@ -20,6 +20,7 @@ import {
IonProgressBar,
IonRow,
IonText,
+ IonTitle,
IonToolbar,
useIonAlert,
useIonModal,
@@ -57,7 +58,6 @@ import {
import { useLoader } from "../context/LoaderContext";
import { getAssetIconUrl } from "../utils/getAssetIconUrl";
import { SymbolIcon } from "./SymbolIcon";
-import { currencyFormat } from "../utils/currency-format";
import { ApyDetail } from "./ApyDetail";
import { AavePool, IAavePool } from "@/pool/Aave.pool";
import { MarketPool } from "@/pool/Market.pool";
@@ -74,6 +74,7 @@ import {
} from "@/utils/getPoolWalletBalance";
import { initializeUserSummary } from "@/store/effects/pools.effect";
import { ModalMessage } from "./ModalMessage";
+import { currencyFormat } from "@/utils/currencyFormat";
interface IReserveDetailProps {
pool: MarketPool;
@@ -82,34 +83,43 @@ interface IReserveDetailProps {
}
const loadTokenData = async (symbol: string) => {
- // check if have localstorage data
+ // check if have localstorage data
const localCoinsListString = localStorage.getItem("coingecko-coins-list");
- let localCoinsList = localCoinsListString ? JSON.parse(localCoinsListString) : null;
+ let localCoinsList = localCoinsListString
+ ? JSON.parse(localCoinsListString)
+ : null;
if (!localCoinsList) {
- localCoinsList = await fetch(`https://api.coingecko.com/api/v3/coins/list`)
- .then((response) => response.json());
- localStorage.setItem("coingecko-coins-list", JSON.stringify(localCoinsList));
+ localCoinsList = await fetch(
+ `https://api.coingecko.com/api/v3/coins/list`
+ ).then((response) => response.json());
+ localStorage.setItem(
+ "coingecko-coins-list",
+ JSON.stringify(localCoinsList)
+ );
}
if (!localCoinsList) {
return;
}
// find coin id by symbol
- const coin = localCoinsList.find((coin: {symbol: string}) => coin.symbol.toLocaleLowerCase() === symbol.toLocaleLowerCase());
+ const coin = localCoinsList.find(
+ (coin: { symbol: string }) =>
+ coin.symbol.toLocaleLowerCase() === symbol.toLocaleLowerCase()
+ );
if (coin) {
// fetch coin data by id
return fetch(`https://api.coingecko.com/api/v3/coins/${coin.id}`)
.then((response) => response.json())
.then((data) => {
console.log("coin data: ", data.description.en);
- const {
- description: {en: description},
+ const {
+ description: { en: description },
market_data: {
- fully_diluted_valuation: {usd: fullyDilutedValuationUSD},
- market_cap: {usd: marketCapUSD},
+ fully_diluted_valuation: { usd: fullyDilutedValuationUSD },
+ market_cap: { usd: marketCapUSD },
max_supply: maxSupply,
total_supply: totalSupply,
- circulating_supply: circulatingSupply
- }
+ circulating_supply: circulatingSupply,
+ },
} = data;
return {
description,
@@ -117,17 +127,18 @@ const loadTokenData = async (symbol: string) => {
marketCapUSD,
maxSupply,
totalSupply,
- circulatingSupply
- }
+ circulatingSupply,
+ };
});
} else {
- return
+ return;
}
-}
+};
export function ReserveDetail(props: IReserveDetailProps) {
const {
pool: { id, chainId },
+ dismiss,
handleSegmentChange,
} = props;
const {
@@ -145,29 +156,29 @@ export function ReserveDetail(props: IReserveDetailProps) {
| undefined
>(undefined);
const poolGroups = Store.useState(getPoolGroupsState);
- const userSummaryAndIncentivesGroup = Store.useState(getUserSummaryAndIncentivesGroupState);
- const [present, dismiss] = useIonToast();
- const [presentAlert] = useIonAlert();
- const [presentSuccess, dismissSuccess] = useIonModal(
- () => (
-
-
-
-
- {state?.actionType.toLocaleUpperCase()} with Success!
-
-
-
- )
+ const userSummaryAndIncentivesGroup = Store.useState(
+ getUserSummaryAndIncentivesGroupState
);
+ const [present, dismissToast] = useIonToast();
+ const [presentAlert] = useIonAlert();
+ const [presentSuccess, dismissSuccess] = useIonModal(() => (
+
+
+
+
+ {state?.actionType.toLocaleUpperCase()} with Success!
+
+
+
+ ));
const [presentPomptCrossModal, dismissPromptCrossModal] = useIonModal(
<>
@@ -217,7 +228,7 @@ export function ReserveDetail(props: IReserveDetailProps) {
>
);
const { display: displayLoader, hide: hideLoader } = useLoader();
- const modal = useRef(null);
+ // const modal = useRef(null);
const [isCrossChain, setIsCrossChain] = useState(false);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isModalOptionsOpen, setIsModalOptionsOpen] = useState(false);
@@ -238,8 +249,9 @@ export function ReserveDetail(props: IReserveDetailProps) {
throw new Error("No poolGroup found");
}
+ console.log('>>>>x x', userSummaryAndIncentivesGroup)
const userSummary = userSummaryAndIncentivesGroup?.find((group) =>
- group.userReservesData.find(({ reserve }) => reserve.id === id)
+ group?.userReservesData?.find(({ reserve }) => reserve.id === id)
);
const pool = poolGroup.pools.find((r) => r.id === id);
@@ -341,7 +353,7 @@ export function ReserveDetail(props: IReserveDetailProps) {
// update userSummary & wallet assets after action
if (walletAddress) {
await initializeUserSummary(walletAddress);
- await loadAssets();
+ await loadAssets(true);
}
};
@@ -488,29 +500,46 @@ export function ReserveDetail(props: IReserveDetailProps) {
// setTokenDetails(() => details);
// });
// }, [pool.symbol]);
-
+
return (
<>
-
-
- props.dismiss(state?.actionType)}
- >
-
-
-
-
-
Market details
-
+
+
+
+ Pool {pool?.symbol}
+
+ {
+ CHAIN_AVAILABLES.find(
+ (c) => c.id === pool.chainId
+ )?.name
+ }{" "} Network
+
+
+
+ dismiss(state?.actionType)}
+ >
+
+
+
+
+
+
+
+
+ Market Details
+
+
-
-
-
- {pool?.symbol}
-
-
- {
- CHAIN_AVAILABLES.find(
- (c) => c.id === pool.chainId
- )?.name
- }{" "}
- network
-
-
-
- {pool.usageAsCollateralEnabled === false && (
-
- )}
-
+
+
+
+
+
+
+
+ {pool?.symbol}
+
+
+ {
+ CHAIN_AVAILABLES.find(
+ (c) => c.id === pool.chainId
+ )?.name
+ }{" "}
+ network
+
+
+
+ {pool.usageAsCollateralEnabled === false && (
+
+ )}
+
+
+
-
+
{walletAddress ? (
<>
{tokenDetails && (
-
+
Token details
@@ -784,14 +838,14 @@ export function ReserveDetail(props: IReserveDetailProps) {
) > 0 && (
<>
- {currencyFormat(
+ {currencyFormat.format(
Number(
protocolSummary?.totalBorrowsUSD ||
0
)
)}{" "}
of{" "}
- {currencyFormat(
+ {currencyFormat.format(
Number(
protocolSummary?.totalCollateralUSD ||
0
@@ -1021,7 +1075,6 @@ export function ReserveDetail(props: IReserveDetailProps) {
{
setIsModalOpen(false);
@@ -1077,7 +1130,7 @@ export function ReserveDetail(props: IReserveDetailProps) {
text: "x",
role: "cancel",
handler: () => {
- dismiss();
+ dismissToast();
},
},
],
diff --git a/src/components/ShowUIButton.tsx b/src/components/ShowUIButton.tsx
deleted file mode 100644
index ce1f3daf..00000000
--- a/src/components/ShowUIButton.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { useEffect, useState } from "react";
-import { IonButton, IonSpinner } from "@ionic/react";
-import { getMagic } from "@/servcies/magic";
-
-const ShowUIButton = () => {
- // Initialize state variable to decide whether to show button or not
- const [isLoading, setIsLoading] = useState(false);
- const [showButton, setShowButton] = useState(false);
- // Define a function to check the type of the wallet
- const checkWalletType = async () => {
- const magic = await getMagic();
- try {
- setIsLoading(true);
- const isMagicProvider = magic.rpcProvider.isMagic;
- if (!isMagicProvider) {
- setIsLoading(false);
- setShowButton(false);
- return;
- }
- // Fetch the wallet's information using Magic's user.getInfo method
-
- ///@ts-ignore
- // Determine if the wallet type is "magic"
- // Set 'showButton' state based on the result of the check
- const isMagicWallet = (await magic.user.getInfo())?.walletType === "magic";
- setIsLoading(false);
- setShowButton(isMagicWallet);
- } catch (error) {
- // Log any errors that occur during the wallet type check process
- setIsLoading(false);
- }
- };
-
- useEffect(() => {
- // Call the checkWalletType function
- checkWalletType();
- return () => {};
- }, []);
-
- // Define the event handler for the button click
- const handleShowUI = async () => {
- try {
- // Try to show the magic wallet user interface
- const magic = await getMagic();
- await magic.wallet.showUI();
- throw new Error("Not implemented");
- } catch (error) {
- // Log any errors that occur during the process
- console.error("handleShowUI:", error);
- }
- };
-
- // Render the button component if showButton is true, otherwise render nothing
- return isLoading ? (
-
- ) : showButton ? (
-
- Wallet overview
-
- ) : null;
-};
-
-export default ShowUIButton;
diff --git a/src/components/SymbolIcon.tsx b/src/components/SymbolIcon.tsx
index 9feaf415..835bc67b 100644
--- a/src/components/SymbolIcon.tsx
+++ b/src/components/SymbolIcon.tsx
@@ -18,7 +18,7 @@ export function SymbolIcon(props: {symbol: string; chainId?: number; iconSize?:
) : null;
return (
void;
}) {
const { connectWallet } = Store.useState(getWeb3State);
+ const router = useIonRouter();
return (
-
-
-
-
-
+
+
+ {/*
+ */}
-
- Hexa Lite
- Build your wealth with cryptoassets
-
-
+
+ {/*
*/}
+
+
+ Hexa Lite
+
+ Build your wealth with{" "}
+
+ crypto assets
+
+
+
+
+ {/*
Buy digitals assets with fiats, exchange at best rate, lend and
borrow money on DeFi protocols without any intermediate smart
contract or third-party to enforce security and increase earn
interest.
-
+ */}
-
- handleSegmentChange({ detail: { value: "swap" } })
- }
- >
- Launch App
-
-
+
+ {
+ router.push("wallet", "forward");
+ handleSegmentChange({ detail: { value: "wallet" } });
+ }}
+ >
+ Launch App
+
+ {
+ router.push("available-platforms", "forward");
+ }}
+ >
+ Available on iOS, Android and Desktop
+
+
+
+
+
+
+
@@ -97,11 +146,9 @@ export function Welcome({
-
+
-
+
- Frictionless onBoarding
+ Create or connect an account
+ under a minute
- Hexa Lite ensures a secure and reliable user experience,
- enabling everyone to own their own assets. Users can enjoy the advantages of blockchain
- technology and DeFi services without the need to manage
- private keys or seed phrases.
+ Get started seamlessly! Connect or create an account in less than a minute.{" "}
+ Whether through social login, email, or your existing wallet, our platform ensures a{" "}
+
+ smooth onboarding experience
+
+ . Take control of your financial path with ease.
-
+
-
@@ -167,7 +218,7 @@ export function Welcome({
style={{ padding: "20vh 0" }}
>
-
+
- Safely deposit your liquidity into DeFi protocols across
- more than 40 markets and{" "}
+ Safely deposit your liquidity{" "}
+
+ without any restrictions or censorship
+ {" "}
+ into DeFi protocols across more than 40 markets and{" "}
{CHAIN_AVAILABLES.filter((c) => c.type === "evm").length}{" "}
EVM-Compatible blockchains. Earn substantial interest
while retaining complete control over your assets.
@@ -189,9 +243,10 @@ export function Welcome({
- handleSegmentChange({ detail: { value: "defi" } })
- }
+ onClick={(e) => {
+ router.push("defi", "forward");
+ handleSegmentChange({ detail: { value: "defi" } });
+ }}
>
Start Deposit
@@ -202,15 +257,14 @@ export function Welcome({
size-md="4"
className="ion-text-center ion-padding"
>
-
+
@@ -223,7 +277,7 @@ export function Welcome({
style={{ padding: "20vh 0" }}
>
-
+
- Unlock the potential of your assets by earn interest
- through Liquid Staking.
- Stake with DeFi protocols without any lockup periods or
- restrictions.
+ Unlock higher earnings without locking your assets!{" "}
+ Liquid Staking offers attractive interest rates on your crypto,{" "}
+ keeping your funds accessible at all times .{" "}
+ It's the perfect balance of liquidity and profitability. Now it's time to maximize your returns.
- handleSegmentChange({ detail: { value: "earn" } })
- }
+ onClick={(e) => {
+ router.push("earn", "forward");
+ handleSegmentChange({ detail: { value: "earn" } });
+ }}
>
Start Earning
@@ -257,7 +312,7 @@ export function Welcome({
size-md="4"
className="ion-text-center ion-padding-vertical"
>
-
+
-
+
- Hexa Lite support Bitcoin, Solana and +20 EVM-Compatible blockchain such as
- Ethereum, Polygon, Binance Smart Chain, Optimism,
- Arbitrum, etc. without have to care about how to manage
- networks changes.
+ Hexa Lite support Bitcoin, Solana and +20 EVM-Compatible
+ blockchain such as Ethereum, Polygon, Binance Smart Chain,
+ Optimism, Arbitrum, etc. without have to care about how to
+ manage networks changes.
@@ -300,18 +355,13 @@ export function Welcome({
size-md="4"
className="ion-text-center ion-padding"
>
-
+
-
+
+ {
+ router.push("about", "forward");
+ }}
+ >
+ About the team
+
-
+
{
window.open(
@@ -430,10 +488,14 @@ export function Welcome({
{/* Partners Section */}
-
+
MAGIC
- {/*
+
@@ -607,17 +669,54 @@ export function Welcome({
height: "65px",
margin: "0.5rem auto",
}}
- src="./assets/images/onramp-logo-emblem.svg"
+ src="./assets/images/mtpelerin.png"
>
- ONRAMP
+ MT.PELERIN
- */}
+
+ {/* Publications */}
+
+
+
+
+
+
+ Latest articles
+
+
+
+
+ from Medium blog
+
+
+
+
+
+
+
+
+
+
+
+
+
{/* FAQ */}
-
+
Earn interest with your assets
@@ -664,9 +763,10 @@ export function Welcome({
className="ion-margin-top"
size="large"
color="gradient"
- onClick={(e) =>
- handleSegmentChange({ detail: { value: "swap" } })
- }
+ onClick={(e) => {
+ router.push("wallet", "forward");
+ handleSegmentChange({ detail: { value: "wallet" } });
+ }}
>
Launch App
diff --git a/src/components/base/WalletBaseContainer.tsx b/src/components/base/WalletBaseContainer.tsx
new file mode 100644
index 00000000..c3d42b97
--- /dev/null
+++ b/src/components/base/WalletBaseContainer.tsx
@@ -0,0 +1,229 @@
+import {
+ IonButton,
+ IonButtons,
+ IonContent,
+ IonHeader,
+ IonIcon,
+ IonModal,
+ IonPage,
+ IonProgressBar,
+ IonTitle,
+ IonToolbar,
+ ModalOptions,
+} from "@ionic/react";
+import React, { Suspense, lazy } from "react";
+import { IAsset } from "@/interfaces/asset.interface";
+import { DepositContainer } from "@/containers/DepositContainer";
+import { HookOverlayOptions } from "@ionic/react/dist/types/hooks/HookOverlayOptions";
+import { TransferContainer } from "../../containers/TransferContainer";
+const BuyWithFiatContainer = lazy(() => import("@/containers/BuyWithFiat"));
+
+export type SelectedTokenDetail = {
+ name: string;
+ symbol: string;
+ priceUsd: number;
+ balance: number;
+ balanceUsd: number;
+ thumbnail: string;
+ assets: IAsset[];
+};
+
+export interface WalletComponentProps {
+ modalOpts: Omit &
+ HookOverlayOptions;
+ walletAddress?: string;
+ assets: IAsset[];
+ loadAssets: (force?: boolean) => Promise;
+}
+
+export interface WalletComponentState {
+ filterBy: string | null;
+ assetGroup: SelectedTokenDetail[];
+ totalBalance: number;
+ selectedTokenDetail: SelectedTokenDetail | null;
+ isEarnModalOpen: boolean;
+ isTransferModalOpen: boolean;
+ isDepositModalOpen: boolean;
+ isBuyWithFiatModalOpen: boolean;
+}
+
+export default class WalletBaseComponent extends React.Component<
+ T & WalletComponentProps,
+ WalletComponentState
+> {
+ constructor(props: T & WalletComponentProps) {
+ super(props);
+ this.state = {
+ filterBy: null,
+ selectedTokenDetail: null,
+ assetGroup: [],
+ totalBalance: 0,
+ isEarnModalOpen: false,
+ isTransferModalOpen: false,
+ isDepositModalOpen: false,
+ isBuyWithFiatModalOpen: false,
+ };
+ }
+
+ componentDidMount() {
+ this.calculateBalance();
+ this.groupAssets();
+ }
+
+ componentDidUpdate(
+ prevProps: Readonly,
+ prevState: Readonly,
+ snapshot?: any
+ ): void {
+ if (prevProps.assets !== this.props.assets) {
+ this.calculateBalance();
+ this.groupAssets();
+ }
+ }
+
+ calculateBalance() {
+ if (!this.props.assets) {
+ this.setState({ totalBalance: 0 });
+ return;
+ }
+ const totalBalance = this.props.assets.reduce((acc, asset) => {
+ return acc + asset.balanceUsd;
+ }, 0);
+ this.setState({ totalBalance });
+ }
+
+ groupAssets() {
+ const assetGroup = [...this.props.assets]
+ ?.sort((a, b) => b.balanceUsd - a.balanceUsd)
+ ?.reduce((acc, asset) => {
+ // check existing asset symbol
+ const symbol =
+ asset.name.toLowerCase().includes("aave") &&
+ asset.name.toLowerCase() !== "aave token"
+ ? asset.name.split(" ").pop() || asset.symbol
+ : asset.symbol;
+ const name =
+ asset.name.toLowerCase().includes("aave") &&
+ asset.name.toLowerCase() !== "aave token"
+ ? asset.name.split(" ").pop() || asset.name
+ : asset.name;
+
+ const index = acc.findIndex((a) => a.symbol === symbol);
+ if (index !== -1) {
+ const balanceUsd =
+ asset.balanceUsd <= 0 && asset.balance > 0
+ ? acc[index].priceUsd * asset.balance
+ : asset.balanceUsd;
+ acc[index].balance += asset.balance;
+ acc[index].balanceUsd += balanceUsd;
+ acc[index].assets.push(asset);
+ } else {
+ acc.push({
+ name: name,
+ symbol: symbol,
+ priceUsd: asset.priceUsd,
+ thumbnail: asset.thumbnail,
+ balance: asset.balance,
+ balanceUsd: asset.balanceUsd,
+ assets: [asset],
+ });
+ }
+ return acc;
+ }, [] as { name: string; symbol: string; priceUsd: number; balance: number; balanceUsd: number; thumbnail: string; assets: IAsset[] }[]);
+ this.setState({ assetGroup });
+ }
+
+ async handleSearchChange(e: CustomEvent) {
+ this.setState({ filterBy: e.detail.value });
+ }
+
+ async handleTokenDetailClick(token: any = null) {
+ console.log(token);
+ this.setState((prev) => ({
+ ...prev,
+ selectedTokenDetail: token,
+ }));
+ }
+
+ async handleEarnClick() {
+ this.setState({ isEarnModalOpen: !this.state.isEarnModalOpen });
+ }
+
+ async handleTransferClick(state: boolean) {
+ console.log("handleTransferClick", state);
+ this.setState({ isTransferModalOpen: state });
+ }
+
+ async handleDepositClick(state?: boolean) {
+ this.setState({
+ isDepositModalOpen:
+ state !== undefined ? state : !this.state.isDepositModalOpen,
+ });
+ }
+
+ async handleRefresh() {
+ this.props.loadAssets(true);
+ }
+
+ async handleBuyWithFiat(state: boolean) {
+ console.log(">>>>>", state);
+
+ this.setState((prev) => ({
+ ...prev,
+ isBuyWithFiatModalOpen: state,
+ }));
+ }
+
+ render(): React.ReactNode {
+ return (
+ <>
+ this.handleTransferClick(false)}
+ >
+ this.handleTransferClick(false)} />
+
+
+ this.handleDepositClick(false)}
+ >
+
+ this.handleBuyWithFiat(state)
+ }
+ dismiss={() => this.handleDepositClick(false)}
+ />
+
+
+ this.handleBuyWithFiat(false)}
+ >
+
+
+
+
+
+ }
+ >
+ this.handleBuyWithFiat(false)}
+ isLightmode={localStorage.getItem('hexa-lite_is-lightmode') === 'true' ? true : undefined}
+ />
+
+
+ >
+ );
+ }
+}
diff --git a/src/components/mobile/ActionNavButtons.tsx b/src/components/mobile/ActionNavButtons.tsx
new file mode 100644
index 00000000..9a2c8570
--- /dev/null
+++ b/src/components/mobile/ActionNavButtons.tsx
@@ -0,0 +1,96 @@
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import {
+ IonCol,
+ IonFab,
+ IonFabButton,
+ IonIcon,
+ IonModal,
+ IonRow,
+ ModalOptions,
+ useIonModal,
+} from "@ionic/react";
+import { HookOverlayOptions } from "@ionic/react/dist/types/hooks/HookOverlayOptions";
+import { paperPlane, download, repeat, card } from "ionicons/icons";
+
+const style = {
+ fullHeight: {
+ height: "100%",
+ },
+ fab: {
+ display: "contents",
+ },
+};
+
+export const MobileActionNavButtons = (props: {
+ hideEarnBtn?: boolean;
+ setState: (state: any) => void;
+ setIsSwapModalOpen: () => void;
+}) => {
+ const {
+ hideEarnBtn = false,
+ setState,
+ setIsSwapModalOpen,
+ } = props;
+
+ const {assets} = Store.useState(getWeb3State);
+ const balance = assets.reduce((prev, curr) => {
+ return prev + curr.balance
+ }, 0);
+
+ return (
+
+
+
+
+ setState({ isTransferModalOpen: true })}
+ >
+
+
+
+
+
+
+
+ setState({ isDepositModalOpen: true })}
+ >
+
+
+
+
+
+
+
+ setIsSwapModalOpen()}
+ >
+
+
+
+
+
+ {hideEarnBtn !== true && (
+
+
+ {
+ setState({ isEarnModalOpen: true });
+ }}
+ >
+
+
+
+
+ )}
+
+ );
+};
diff --git a/src/components/mobile/WalletAssetsList.tsx b/src/components/mobile/WalletAssetsList.tsx
new file mode 100644
index 00000000..bf933ff4
--- /dev/null
+++ b/src/components/mobile/WalletAssetsList.tsx
@@ -0,0 +1,158 @@
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import {
+ IonAvatar,
+ IonCol,
+ IonIcon,
+ IonItem,
+ IonItemOption,
+ IonItemOptions,
+ IonItemSliding,
+ IonLabel,
+ IonList,
+ IonRow,
+ IonText,
+} from "@ionic/react";
+import { SelectedTokenDetail } from "../base/WalletBaseContainer";
+import { getAssetIconUrl } from "@/utils/getAssetIconUrl";
+import { currencyFormat } from "@/utils/currencyFormat";
+import { paperPlane, repeat } from "ionicons/icons";
+
+export const WalletAssetsList = (props: {
+ totalBalance: number;
+ assetGroup: SelectedTokenDetail[];
+ filterBy: string;
+ handleTokenDetailClick: (asset: SelectedTokenDetail) => Promise;
+ handleTransferClick: (asset: SelectedTokenDetail) => Promise;
+ setIsSwapModalOpen: (asset: SelectedTokenDetail) => Promise;
+}) => {
+ const {
+ totalBalance, assetGroup, filterBy,
+ handleTokenDetailClick, handleTransferClick, setIsSwapModalOpen
+ } = props;
+ const { walletAddress, assets, isMagicWallet, loadAssets } =
+ Store.useState(getWeb3State);
+
+ return (
+ <>
+ {totalBalance > 0 && (
+
+
+
+ {assetGroup
+ .filter((asset) =>
+ filterBy
+ ? asset.symbol
+ .toLowerCase()
+ .includes(filterBy.toLowerCase())
+ : true
+ )
+ .sort((a, b) => (a.balanceUsd > b.balanceUsd ? -1 : 1))
+ .map((asset, index) => (
+
+ {
+ console.log("handleTokenDetailClick: ", asset);
+ handleTokenDetailClick(asset);
+ }}
+ >
+
+ {
+ (
+ event.target as any
+ ).src = `https://images.placeholders.dev/?width=42&height=42&text=${asset.symbol}&bgColor=%23000000&textColor=%23182449`;
+ }}
+ />
+
+
+
+ {asset.symbol}
+
+
+
+ {asset.name}
+
+
+
+
+
+ {currencyFormat.format(asset.balanceUsd)}
+
+
+ {asset.balance.toFixed(6)}
+
+
+
+
+ {
+ // close the sliding item after clicking the option
+ (event.target as HTMLElement)
+ .closest("ion-item-sliding")
+ ?.close();
+ }}
+ >
+ {
+ handleTransferClick(asset);
+ }}
+ >
+
+
+ {
+ setIsSwapModalOpen(asset);
+ }}
+ >
+
+
+
+
+ ))}
+
+
+
+ )}
+ >
+ );
+};
diff --git a/src/components/ui/AppLogo.tsx b/src/components/ui/AppLogo.tsx
new file mode 100644
index 00000000..0320681f
--- /dev/null
+++ b/src/components/ui/AppLogo.tsx
@@ -0,0 +1,51 @@
+import { IonIcon, IonImg } from "@ionic/react";
+import React, { useEffect, useMemo, useRef, useState } from "react";
+
+const styleLogo = {
+ padding: " 0px",
+ width: "42px",
+ maxWidth: "42px",
+ height: "42px",
+ cursor: "pointer",
+};
+
+export const AppLogo = (props: {
+ width?: string;
+ height?: string;
+ handleClick?: () => void;
+ style?: any;
+}) => {
+ const {
+ handleClick,
+ height = styleLogo.height,
+ width = styleLogo.width,
+ style,
+ } = props;
+
+ return (
+ <>
+
+ {/* */}
+ >
+ );
+};
diff --git a/src/components/ui/Currency.tsx b/src/components/ui/Currency.tsx
new file mode 100644
index 00000000..534c2d1d
--- /dev/null
+++ b/src/components/ui/Currency.tsx
@@ -0,0 +1,17 @@
+import Store from "@/store";
+import { getAppSettings } from "@/store/selectors";
+import { currencyFormat } from "@/utils/currencyFormat";
+
+export const Currency = (props: {
+ value: number,
+}) => {
+ const { value } = props;
+ const { ui: {hideCurrencieAmount} } = Store.useState(getAppSettings)
+
+ const data = hideCurrencieAmount
+ ? '*****'
+ : currencyFormat.format(value)
+ return (<>
+ {data}
+ >)
+}
\ No newline at end of file
diff --git a/src/components/ui/InstallPWASteps.tsx b/src/components/ui/InstallPWASteps.tsx
new file mode 100644
index 00000000..e0c830ce
--- /dev/null
+++ b/src/components/ui/InstallPWASteps.tsx
@@ -0,0 +1,51 @@
+import { IonButton, IonCol, IonGrid, IonRow, IonText, isPlatform } from "@ionic/react";
+
+export const InstallPWASteps = (props: {
+ setIsInstallModalOpen: (state: boolean)=> void;
+}) => {
+
+ return (
+ <>
+
+
+
+
+
+ Install to enable all features!
+
+
+ Earn strategies, loans market, send, deposit and buy crypto with fiat.
+
+
+
+ {isPlatform('ios') && (
+ <>
+ Tap the Share button at the bottom of the browser
+ Scroll down and select "Add to Home Screen."
+ Tap "Add" in the top right corner
+ >
+ )}
+ {!isPlatform('ios') && (
+ <>
+ Tap the menu button (three dots) in your browserr
+ Select "Add to Home Screen" or "Install App."
+ >
+ )}
+
+
+ Enjoy instant access to our app with a single tap!
+
+
+
+ props.setIsInstallModalOpen(false)}>ok
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/src/components/ui/LightChart.tsx b/src/components/ui/LightChart.tsx
new file mode 100644
index 00000000..d2ab6481
--- /dev/null
+++ b/src/components/ui/LightChart.tsx
@@ -0,0 +1,94 @@
+import { ColorType, IChartApi, createChart } from 'lightweight-charts';
+import { useEffect, useRef } from 'react';
+
+// Generated by https://quicktype.io
+
+export interface DataItem {
+ time: string;
+ value: number;
+}
+
+export default function LightChart(props: { data: DataItem[], minHeight?: number }) {
+ const chartContainerRef = useRef(null);
+ const chartRef = useRef(null);
+
+
+ useEffect(() => {
+ if (chartContainerRef.current && props.data.length > 0) {
+ // remove the chart if it already exists
+ if (chartRef.current) {
+ chartRef.current.remove();
+ chartRef.current = null;
+ }
+
+ // create a new chart
+ const chart = createChart(chartContainerRef.current, {
+ width: window.innerWidth||400,
+ height: props?.minHeight || 250,
+ layout: {
+ background: {
+ type: ColorType.Solid,
+ color: 'transparent',
+ },
+ textColor: '#d1d4dc',
+ },
+ grid: {
+ vertLines: {
+ visible: false,
+ },
+ horzLines: {
+ // color: 'rgba(42, 46, 57, 0.05)',
+ visible: false
+ },
+ },
+ rightPriceScale: {
+ borderVisible: false,
+ visible: true,
+ },
+ timeScale: {
+ borderVisible: false,
+ fixLeftEdge: true,
+ rightBarStaysOnScroll: true,
+ fixRightEdge: true,
+ },
+ crosshair: {
+ horzLine: {
+ // visible: false,
+ style: 4,
+ },
+ },
+ autoSize: true,
+
+ });
+ // const lineSeries = chart.addLineSeries();
+ // lineSeries.setData(props.data);
+
+ const series = chart.addAreaSeries({
+ topColor: 'rgba(0,144,255, 0.618)',
+ bottomColor: 'rgba(0,144,255, 0.01)',
+ lineColor: 'rgba(0,144,255, 1)',
+ lineWidth: 3,
+ });
+ series.setData(props.data);
+
+ const now = new Date();
+ chart.timeScale().setVisibleRange({
+ from: new Date(`${now.getFullYear()}-${(now.getMonth()+1) < 10 ? `0${(now.getMonth() + 1)}` : now.getMonth()+1}-${now.getDate()-7}`).getTime() / 1000 as any,
+ to: now.getTime() / 1000 as any,
+ });
+
+ // store the chart instance in the ref
+ chartRef.current = chart;
+ }
+ // clean up the chart when the component is unmounted
+ return () => {
+ if (chartRef.current) {
+ chartRef.current.remove();
+ chartRef.current = null;
+ }
+ };
+ }, [props.data]);
+
+ return
;
+};
+
diff --git a/src/components/ui/MenuSetting.tsx b/src/components/ui/MenuSetting.tsx
new file mode 100644
index 00000000..462e41d1
--- /dev/null
+++ b/src/components/ui/MenuSetting.tsx
@@ -0,0 +1,300 @@
+import React, { useRef, useState } from "react";
+import {
+ IonMenu,
+ IonHeader,
+ IonToolbar,
+ IonTitle,
+ IonContent,
+ IonItem,
+ IonIcon,
+ IonLabel,
+ IonText,
+ IonItemDivider,
+ IonButton,
+ IonModal,
+ IonFooter,
+ IonNote,
+ IonButtons,
+ useIonRouter,
+ IonPopover,
+} from "@ionic/react";
+import { close, open, openOutline, radioButtonOn, ribbonOutline } from "ionicons/icons";
+import { getAddressPoints } from "@/servcies/datas.service";
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import ConnectButton from "../ConnectButton";
+import DisconnectButton from "../DisconnectButton";
+import { ToggleLightmode } from "./ToogleLightmode";
+import { PointsPopover } from "../PointsPopover";
+
+interface MenuSettingsProps {
+ dismiss: ()=> void
+}
+
+export const MenuSettings: React.FC = ({dismiss}) => {
+ const { walletAddress } = Store.useState(getWeb3State);
+ const [points, setPoints] = useState(null);
+ const [isPointsPopoverOpen, setIsPointsPopoverOpen] = useState(false);
+ const pointsPopoverRef = useRef(null);
+ const router = useIonRouter();
+
+ const openPopover = (e: any) => {
+ pointsPopoverRef.current!.event = e;
+ setIsPointsPopoverOpen(true);
+ };
+
+ return (
+ <>
+
+
+ Settings
+
+ {
+ dismiss();
+ }}>
+
+
+
+
+
+
+ {
+ dismiss();
+ }}
+ >
+
+
+ Wallet
+
+
+
+ {walletAddress}
+
+
+
+
+ Connected
+
+
+
+
+
+ Points
+
+
+
+ Rank to the leaderboard
+
+
+
+ openPopover(e)}
+ >
+
+
+ Points
+
+
+
+ {
+ setPoints(() => null);
+ setIsPointsPopoverOpen(false);
+ }}
+ onWillPresent={async () => {
+ if (!walletAddress) {
+ throw new Error('Wallet not connected')
+ }
+ const response = await getAddressPoints(
+ walletAddress
+ ).catch((error) => {});
+ console.log("response", response);
+ if (response?.data?.totalPoints) {
+ setPoints(() => response.data.totalPoints);
+ } else {
+ setPoints(() => "0");
+ }
+ }}
+ >
+ setIsPointsPopoverOpen(false)}
+ />
+
+
+
+
+
+ Dark mode
+
+
+
+ Enable or disable dark mode
+
+
+
+
+
+
+
+
+ Feedback
+
+
+
+ Send your feedback
+
+
+
+ {
+ window.open('https://forms.gle/Dx25eG66TMxyFfh8A', '_blank')
+ }}>
+
+
+
+
+
+
+ Gouvernance
+
+
+
+ Snapshot
+
+
+
+ {
+ window.open('https://snapshot.org/#/hexaonelabs.eth', '_blank')
+ }}>
+
+
+
+
+
+
+ Source code
+
+
+
+ Github
+
+
+
+ {
+ window.open('https://github.com/hexaonelabs', '_blank')
+ }}>
+
+
+
+
+
+
+ Terms & Conditions
+
+
+
+ PDF
+
+
+
+ {
+ window.open('https://hexa-lite.io/terms-conditions.pdf', '_blank')
+ }}>
+
+
+
+
+
+
+ Wallet key export
+
+
+
+ Wallet Magik
+
+
+
+ {
+ window.open('https://wallet.magic.link/', '_blank')
+ }}>
+
+
+
+
+
+
+ Version
+
+
+
+ https://hexa-lite.io
+
+
+
+
+ {process.env.NEXT_PUBLIC_APP_VERSION}
+
+ {process.env.NEXT_PUBLIC_APP_BUILD_DATE}
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/components/ui/MoonpayOnramp.tsx b/src/components/ui/MoonpayOnramp.tsx
new file mode 100644
index 00000000..646908ec
--- /dev/null
+++ b/src/components/ui/MoonpayOnramp.tsx
@@ -0,0 +1,57 @@
+
+import { loadMoonPay } from '@moonpay/moonpay-js';
+// import { MoonPayProvider, MoonPayBuyWidget } from '@moonpay/moonpay-react';
+import { ReactNode, useEffect, useState } from 'react';
+
+
+export default function MoonpayOnramp(props?: {
+ walletAddress?: string;
+ children?: ReactNode,
+}) {
+ // const [visible, setVisible] = useState(false);
+ const [moonPaySdk, setMoonPaySdk] = useState(undefined as any);
+ const element = props?.children || (Toggle widget );
+
+ useEffect(() => {
+ if (moonPaySdk) {
+ return;
+ }
+ loadMoonPay().then((moonPay) => {
+ if (!moonPay) {
+ return null;
+ }
+ const moonPaySdk = moonPay({
+ flow: 'buy',
+ environment: 'sandbox',
+ variant: 'overlay',
+ params: {
+ apiKey: '',
+ theme: 'dark',
+ baseCurrencyCode: 'usd',
+ baseCurrencyAmount: '100',
+ defaultCurrencyCode: 'eth'
+ }
+ });
+ setMoonPaySdk(moonPaySdk);
+ })
+ }, [moonPaySdk]);
+
+ return (
+ //
+ //
+ moonPaySdk?.show()}>
+ {element}
+
+ //
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/ui/PublicationsList.tsx b/src/components/ui/PublicationsList.tsx
new file mode 100644
index 00000000..2aa37341
--- /dev/null
+++ b/src/components/ui/PublicationsList.tsx
@@ -0,0 +1,54 @@
+import { getPublications } from "@/servcies/medium.service";
+import { IonButton, IonCol, IonImg, IonSpinner, IonText } from "@ionic/react";
+import { useEffect, useState } from "react";
+
+type Publication = {
+ url: string;
+ title: string;
+ imgUrl: string;
+ dateTime: number;
+ short: string;
+};
+export const PublicationsList = () => {
+ const [publications, setPublications] = useState([]);
+
+ useEffect(() => {
+ getPublications().then((result) => setPublications(() => result));
+ }, []);
+
+ return publications.length <= 0 ? (
+
+ ) : (
+ <>
+ {publications.map((p, i) => (
+ window.open(p.url, "_blank")}
+ >
+
+
+ {p.title.replace('Hexa Lite:', '')}
+
+
+ {p.short.slice(0, 150)}... [read more]
+
+
+ ))}
+
+
+
+ SEE ALL ARTICLES
+
+
+
+ >
+ );
+};
diff --git a/src/components/ui/ToggleHideCurrencyAmount.tsx b/src/components/ui/ToggleHideCurrencyAmount.tsx
new file mode 100644
index 00000000..3122cedb
--- /dev/null
+++ b/src/components/ui/ToggleHideCurrencyAmount.tsx
@@ -0,0 +1,35 @@
+import Store from "@/store";
+import { patchAppSettings } from "@/store/actions";
+import { getAppSettings } from "@/store/selectors";
+import { IonIcon } from "@ionic/react";
+import { eyeOffOutline, eyeOutline } from "ionicons/icons";
+
+export const ToggleHideCurrencyAmount = () => {
+ const appSettings = Store.useState(getAppSettings);
+ const {
+ ui: { hideCurrencieAmount}
+ } = appSettings;
+ return (
+ <>
+ {
+ patchAppSettings({
+ ui: {
+ hideCurrencieAmount: !hideCurrencieAmount,
+ },
+ });
+ localStorage.setItem('hexa-lite_app_settings', JSON.stringify({
+ ...appSettings,
+ ui: {
+ ...appSettings.ui,
+ hideCurrencieAmount: !hideCurrencieAmount,
+ }
+ }))
+ }}
+ />
+ >
+ );
+};
diff --git a/src/components/ui/TokenDetailDescription.tsx b/src/components/ui/TokenDetailDescription.tsx
new file mode 100644
index 00000000..1b28aec4
--- /dev/null
+++ b/src/components/ui/TokenDetailDescription.tsx
@@ -0,0 +1,31 @@
+import { TokenInfo } from "@/utils/getTokenInfo";
+import { IonChip, IonLabel, IonListHeader, IonText } from "@ionic/react";
+
+export function TokenDetailDescription(props: { tokenInfo: TokenInfo }) {
+ const { tokenInfo } = props;
+
+ return (
+ <>
+
+
+ Description
+
+
+
+ {tokenInfo.description.en}
+
+
+
+
+ Categories
+
+
+
+ {tokenInfo.categories.map((categorie, i) => (
+ {categorie}
+ ))}
+
+
+ >
+ );
+}
diff --git a/src/components/ui/TokenDetailMarketData.tsx b/src/components/ui/TokenDetailMarketData.tsx
new file mode 100644
index 00000000..dc7cecd0
--- /dev/null
+++ b/src/components/ui/TokenDetailMarketData.tsx
@@ -0,0 +1,243 @@
+import { currencyFormat } from "@/utils/currencyFormat";
+import { TokenInfo } from "@/utils/getTokenInfo";
+import { numberFormat } from "@/utils/numberFormat";
+import {
+ IonItem,
+ IonLabel,
+ IonList,
+ IonListHeader,
+ IonNote,
+ IonText,
+} from "@ionic/react";
+
+export function TokenDetailMarketDetail(props: {
+ tokenInfo: TokenInfo
+}) {
+ const { tokenInfo } = props;
+ return (
+ <>
+
+
+
+ Market details
+
+
+ {Boolean(tokenInfo.market_data.market_cap.usd) && (
+
+ Market Cap.
+
+ {currencyFormat.format(tokenInfo.market_data.market_cap.usd)}
+
+
+ )}
+ {Boolean(tokenInfo.market_data.fully_diluted_valuation.usd) && (
+
+ Fully Diluted Valuation
+
+ {currencyFormat.format(
+ tokenInfo.market_data.fully_diluted_valuation.usd
+ )}
+
+
+ )}
+ {Boolean(tokenInfo.market_data.circulating_supply) && (
+
+ Circulating supply
+
+ {numberFormat.format(tokenInfo.market_data.circulating_supply)}
+
+
+ )}
+ {Boolean(tokenInfo.market_data.total_supply) && (
+
+ Total supply
+
+ {numberFormat.format(tokenInfo.market_data.total_supply)}
+
+
+ )}
+ {Boolean(tokenInfo.market_data.max_supply) && (
+
+ Max supply
+
+ {numberFormat.format(tokenInfo.market_data.max_supply)}
+
+
+ )}
+
+
+
+
+ Historical Price
+
+
+ {Boolean(tokenInfo.market_data.current_price.usd) && (
+
+ Current price
+
+ {currencyFormat.format(tokenInfo.market_data.current_price.usd)}
+
+ {numberFormat.format(
+ tokenInfo.market_data.price_change_percentage_24h_in_currency
+ .usd
+ )}
+ %
+
+
+
+ (24h change)
+
+
+
+
+ )}
+ {Boolean(tokenInfo.market_data.ath.usd) && (
+
+ All time height
+
+ {currencyFormat.format(tokenInfo.market_data.ath.usd)}
+
+ {numberFormat.format(
+ tokenInfo.market_data.ath_change_percentage.usd
+ )}
+ %
+
+
+
+
+ {new Date(
+ tokenInfo.market_data.ath_date.usd
+ ).toLocaleDateString()}
+
+
+
+
+ )}
+ {Boolean(tokenInfo.market_data.atl.usd) && (
+
+ All time low
+
+ {currencyFormat.format(tokenInfo.market_data.atl.usd)}
+
+ {numberFormat.format(
+ tokenInfo.market_data.atl_change_percentage.usd
+ )}
+ %
+
+
+
+
+ {new Date(
+ tokenInfo.market_data.atl_date.usd
+ ).toLocaleDateString()}
+
+
+
+
+ )}
+
+ >
+ );
+}
diff --git a/src/components/ui/ToogleLightmode.tsx b/src/components/ui/ToogleLightmode.tsx
new file mode 100644
index 00000000..d620a6a2
--- /dev/null
+++ b/src/components/ui/ToogleLightmode.tsx
@@ -0,0 +1,28 @@
+import { IonToggle } from "@ionic/react";
+import { useEffect, useState } from "react";
+
+export const ToggleLightmode = () => {
+ const [isLightmode, setIsLightmode] = useState(
+ !document.querySelector('body')?.classList.contains('dark')
+ );
+
+ function handleToggle() {
+ if (typeof window !== 'undefined' && window.localStorage) {
+ const hasData = localStorage.getItem('hexa-lite_is-lightmode');
+ if (hasData && hasData === 'true') {
+ localStorage.setItem('hexa-lite_is-lightmode', 'false');
+ } else {
+ localStorage.setItem('hexa-lite_is-lightmode', 'true');
+ }
+ setIsLightmode(!Boolean(hasData) ? true : false);
+ document.querySelector('body')?.classList.toggle('dark')
+ }
+ }
+
+ return (<>
+
+ >);
+}
\ No newline at end of file
diff --git a/src/components/ui/WalletAssetEntity.tsx b/src/components/ui/WalletAssetEntity.tsx
new file mode 100644
index 00000000..2d55fe90
--- /dev/null
+++ b/src/components/ui/WalletAssetEntity.tsx
@@ -0,0 +1,113 @@
+import { IAsset } from "@/interfaces/asset.interface";
+import { currencyFormat } from "@/utils/currencyFormat";
+import { getAssetIconUrl } from "@/utils/getAssetIconUrl";
+import { numberFormat } from "@/utils/numberFormat";
+import {
+ IonAvatar,
+ IonChip,
+ IonCol,
+ IonGrid,
+ IonLabel,
+ IonRow,
+ IonText,
+} from "@ionic/react";
+import { SelectedTokenDetail } from "../base/WalletBaseContainer";
+import { isStableAsset } from "@/utils/isStableAsset";
+import { Currency } from "./Currency";
+
+export function WalletAssetEntity(props: {
+ asset: SelectedTokenDetail;
+ setSelectedTokenDetail: (asset: SelectedTokenDetail) => void;
+}) {
+ const { asset, setSelectedTokenDetail } = props;
+
+ return (
+ {
+ setSelectedTokenDetail(asset);
+ }}
+ style={{
+ cursor: "pointer",
+ borderBottom: "solid 1px rgba(var(--ion-color-primary-rgb), 0.2)",
+ }}
+ >
+
+
+
+ {
+ (
+ event.target as any
+ ).src = `https://images.placeholders.dev/?width=42&height=42&text=${asset.symbol}&bgColor=%23000000&textColor=%23182449`;
+ }}
+ />
+
+
+ {asset.symbol}
+
+ {asset.name}
+
+
+ {isStableAsset(asset.symbol) ? (
+ stable
+ ) : ''}
+
+
+
+
+
+
+ {currencyFormat.format(asset.priceUsd)}
+
+
+
+
+ {numberFormat.format(asset.balance)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/constants/chains.ts b/src/constants/chains.ts
index 81dbd827..9f672ff8 100644
--- a/src/constants/chains.ts
+++ b/src/constants/chains.ts
@@ -29,7 +29,7 @@ export interface IChain {
const CHAINS_DISABLED = [
NETWORK.cosmos,
- NETWORK.avalanche,
+ // NETWORK.avalanche,
NETWORK.polkadot,
]
@@ -165,20 +165,21 @@ export const CHAIN_AVAILABLES: IChain[] = [
)?.url||'',
type: 'cosmos',
},
- // {
- // id: NETWORK.avalanche,
- // value: 'avalanche',
- // name: 'Avalanche',
- // nativeSymbol: 'AVAX',
- // logo: '/assets/cryptocurrency-icons/avax.svg',
- // rpcUrl: [
- // {primary: false, url:'https://avalanche-c-chain.publicnode.com'},
- // {primary: true, url: "https://rpc.ankr.com/avalanche"}
- // ]
- // .find(
- // (rpc) => rpc.primary
- // )?.url||'',
- // },
+ {
+ id: NETWORK.avalanche,
+ value: 'avalanche',
+ name: 'Avalanche',
+ nativeSymbol: 'AVAX',
+ logo: '/assets/cryptocurrency-icons/avax.svg',
+ rpcUrl: [
+ {primary: false, url:'https://avalanche-c-chain.publicnode.com'},
+ {primary: true, url: "https://rpc.ankr.com/avalanche"}
+ ]
+ .find(
+ (rpc) => rpc.primary
+ )?.url||'',
+ type: 'evm',
+ },
// testnets
// {
// id: 5,
diff --git a/src/containers/BuyWithFiat.tsx b/src/containers/BuyWithFiat.tsx
new file mode 100644
index 00000000..6a720aea
--- /dev/null
+++ b/src/containers/BuyWithFiat.tsx
@@ -0,0 +1,51 @@
+import {
+ IonButton,
+ IonButtons,
+ IonContent,
+ IonHeader,
+ IonIcon,
+ IonTitle,
+ IonToolbar,
+} from "@ionic/react";
+import { close } from "ionicons/icons";
+
+export default function BuyWithFiat(props: {
+ isLightmode?: boolean;
+ dismiss: () => void;
+}) {
+ const url = `https://widget.mtpelerin.com/?_ctkn=57112584-7191-4d1b-8d90-28c7c800f3ea&type=web&tabs=buy${
+ props.isLightmode ? "" : "&mode=dark"
+ }&dnet=optimism_mainnet&bdc=ETH&net=optimism_mainnet&nets=optimism_mainnet&primary=%230090FF`;
+ console.log(props?.isLightmode)
+ return (
+ <>
+
+
+
+ Buy
+
+
+ {
+ props.dismiss();
+ }}
+ >
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/containers/DepositContainer.tsx b/src/containers/DepositContainer.tsx
new file mode 100644
index 00000000..d59c1ec8
--- /dev/null
+++ b/src/containers/DepositContainer.tsx
@@ -0,0 +1,220 @@
+import { CHAIN_AVAILABLES, CHAIN_DEFAULT } from "@/constants/chains";
+import { getQrcodeAsSVG } from "@/servcies/qrcode.service";
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import {
+ IonButton,
+ IonButtons,
+ IonCol,
+ IonContent,
+ IonFooter,
+ IonGrid,
+ IonHeader,
+ IonIcon,
+ IonRow,
+ IonText,
+ IonTitle,
+ IonToolbar,
+ useIonModal,
+} from "@ionic/react";
+import { useEffect, useState } from "react";
+import { close, copyOutline, scan } from 'ionicons/icons';
+import { SuccessCopyAddress } from "@/components/SuccessCopyAddress";
+import { useLoader } from "@/context/LoaderContext";
+import { SelectNetwork } from "@/components/SelectNetwork";
+
+export const DepositContainer = (props: {
+ dismiss: ()=> Promise;
+ handleBuyWithFiat: (state: boolean)=> Promise;
+}) => {
+ const {
+ currentNetwork,
+ walletAddress,
+ switchNetwork,
+ isMagicWallet
+ } = Store.useState(getWeb3State);
+ const [qrCodeSVG, setQrCodeSVG] = useState(null);
+ const chain =
+ CHAIN_AVAILABLES.find((chain) => chain.id === currentNetwork) ||
+ CHAIN_DEFAULT;
+ const [presentSuccessCopyAddress, dismissSuccessCopyAddress] = useIonModal(
+ () => (
+
+ )
+ );
+ const [presentSelectNetwork, dismissSelectNetwork] = useIonModal(() => (
+
+ ));
+ const { display: displayLoader, hide: hidLoader } = useLoader();
+
+ const handleActions = async (type: string, payload?: string) => {
+ await displayLoader();
+ switch (true) {
+ case type === "copy": {
+ if (!payload) return;
+ navigator?.clipboard?.writeText(payload);
+ // display toast confirmation
+ presentSuccessCopyAddress({
+ cssClass: "modalAlert",
+ onDidDismiss(event) {
+ console.log("onDidDismiss", event.detail.role);
+ if (!event.detail.role || event?.detail?.role === "cancel") return;
+ handleActions(event.detail.role, payload);
+ },
+ });
+ break;
+ }
+ case type === "selectNetwork": {
+ presentSelectNetwork({
+ cssClass: "modalAlert",
+ onDidDismiss(event) {
+ if (!event.detail.role || event?.detail?.role === "cancel") return;
+ handleActions(event.detail.role, event.detail.data).then(() =>
+ hidLoader()
+ );
+ },
+ });
+ break;
+ }
+ case type === "getAddressFromNetwork": {
+ await switchNetwork(Number(payload));
+ dismissSelectNetwork(null, "cancel");
+ await handleActions("copy", `${walletAddress}`);
+ break;
+ }
+ case type === 'buy': {
+ props.handleBuyWithFiat(true);
+ }
+ default:
+ break;
+ }
+ await hidLoader();
+ };
+
+ useEffect(() => {
+ if (!walletAddress) {
+ return;
+ }
+ getQrcodeAsSVG(walletAddress).then((url) => {
+ // convet string to SVG
+ const svg = new DOMParser().parseFromString(
+ url as string,
+ "image/svg+xml"
+ );
+ console.log(svg.documentElement);
+ setQrCodeSVG(svg.documentElement as unknown as SVGElement);
+ });
+ }, [walletAddress]);
+
+ return (
+ <>
+
+
+
+ Deposit
+
+
+ {
+ props.dismiss();
+ }}>
+
+
+
+
+
+
+
+
+ Wallet Address
+
+ handleActions("copy", walletAddress || "")} style={{ cursor: "pointer" }}>
+ {walletAddress?.slice(0, 6)}...{walletAddress?.slice(walletAddress.length - 6, walletAddress.length)}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ You can send token to all this following networks that
+ are available to this address
+
+
+
+
+ {CHAIN_AVAILABLES.filter((c) => c.type === "evm").map(
+ (chain, index) => (
+
+
+
+ )
+ )}
+
+
+
+
+ {/*
+
+
+
+ Scan QR Code
+
+
+ */}
+
+
+
+
+ {
+ handleActions('buy');
+ }}>
+ Buy Crypto
+
+
+
+ >
+ );
+};
diff --git a/src/containers/ErrorBoundary.tsx b/src/containers/ErrorBoundary.tsx
new file mode 100644
index 00000000..2bd25a7b
--- /dev/null
+++ b/src/containers/ErrorBoundary.tsx
@@ -0,0 +1,40 @@
+import React from "react"
+
+class ErrorBoundary extends React.Component {
+ constructor(props: any ) {
+ super(props)
+
+ // Define a state variable to track whether is an error or not
+ this.state = { hasError: false }
+ }
+ static getDerivedStateFromError(error: Error) {
+ // Update state so the next render will show the fallback UI
+
+ return { hasError: true }
+ }
+ componentDidCatch(error: Error, errorInfo: any) {
+ // You can use your own error logging service here
+ console.log({ error, errorInfo })
+ }
+ render() {
+ // Check if the error is thrown
+ if (this.state.hasError) {
+ // You can render any custom fallback UI
+ return (
+
+
Oops, something went wrong...
+ this.setState({ hasError: false })}
+ >
+ Try again?
+
+
+ )
+ }
+ // Return children components in case of no error
+ return this.props.children
+ }
+}
+
+export default ErrorBoundary
\ No newline at end of file
diff --git a/src/containers/TransferContainer.tsx b/src/containers/TransferContainer.tsx
new file mode 100644
index 00000000..ef42af0c
--- /dev/null
+++ b/src/containers/TransferContainer.tsx
@@ -0,0 +1,535 @@
+import { IAsset } from "@/interfaces/asset.interface";
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import {
+ IonButton,
+ IonButtons,
+ IonCol,
+ IonContent,
+ IonFab,
+ IonFabButton,
+ IonGrid,
+ IonHeader,
+ IonIcon,
+ IonInput,
+ IonItem,
+ IonLabel,
+ IonList,
+ IonListHeader,
+ IonModal,
+ IonPopover,
+ IonRow,
+ IonText,
+ IonTitle,
+ IonToolbar,
+} from "@ionic/react";
+import { chevronDown, close, scan } from "ionicons/icons";
+import { SymbolIcon } from "../components/SymbolIcon";
+import {
+ Dispatch,
+ SetStateAction,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from "react";
+import { CHAIN_AVAILABLES, CHAIN_DEFAULT } from "@/constants/chains";
+import { getReadableAmount } from "@/utils/getReadableAmount";
+import { InputInputEventDetail, IonInputCustomEvent } from "@ionic/core";
+import { Html5Qrcode } from "html5-qrcode";
+
+const isNumberKey = (evt: React.KeyboardEvent) => {
+ var charCode = evt.which ? evt.which : evt.keyCode;
+ return !(charCode > 31 && (charCode < 48 || charCode > 57));
+};
+
+const scanQrCode = async (
+ html5QrcodeScanner: Html5Qrcode
+): Promise => {
+ try {
+ const qrboxFunction = function (
+ viewfinderWidth: number,
+ viewfinderHeight: number
+ ) {
+ // Square QR Box, with size = 80% of the min edge width.
+ const size = Math.min(viewfinderWidth, viewfinderHeight) * 0.8;
+ return {
+ width: size,
+ height: size,
+ };
+ };
+ const cameras = await Html5Qrcode.getCameras();
+ if (!cameras || cameras.length === 0) {
+ throw new Error("No camera found");
+ }
+
+ // get prefered back camera if available or load the first one
+ const cameraId =
+ cameras.find((c) => c.label.toLowerCase().includes("rear"))?.id || cameras[0].id;
+ console.log(">>", cameraId, cameras);
+ // start scanner
+ const config = {
+ fps: 10,
+ qrbox: qrboxFunction,
+ // Important notice: this is experimental feature, use it at your
+ // own risk. See documentation in
+ // mebjas@/html5-qrcode/src/experimental-features.ts
+ experimentalFeatures: {
+ useBarCodeDetectorIfSupported: true,
+ },
+ rememberLastUsedCamera: true,
+ showTorchButtonIfSupported: true,
+ };
+ if (!cameraId) {
+ throw new Error("No camera found");
+ }
+ // If you want to prefer front camera
+ return new Promise((resolve, reject) => {
+ html5QrcodeScanner.start(
+ cameraId,
+ config,
+ (decodedText, decodedResult) => {
+ // stop reader
+ html5QrcodeScanner.stop();
+ // resolve promise with the decoded text
+ resolve(decodedText);
+ },
+ (error) => {}
+ );
+ });
+ } catch (error: any) {
+ throw new Error(error?.message || "BarcodeScanner not available");
+ }
+};
+
+const ScanModal = (props: {
+ isOpen: boolean;
+ onDismiss: (address?: string) => void;
+}) => {
+ const [html5Qrcode, setHtml5Qrcode] = useState();
+ const elementRef = useRef(null);
+
+ useEffect(() => {
+ if (!props.isOpen) {
+ return;
+ }
+ console.log(">>>>", elementRef.current);
+ if (!elementRef.current) {
+ return;
+ }
+ if (!html5Qrcode) {
+ throw new Error("BarcodeScanner not available");
+ }
+ const scaner = new html5Qrcode("reader-scan-element");
+ if (!scaner) {
+ throw new Error("BarcodeScanner not loaded");
+ }
+ try {
+ scanQrCode(scaner).then((result) => {
+ scaner.stop();
+ props.onDismiss(result);
+ });
+ } catch (error: any) {
+ console.error(error);
+ scaner.stop();
+ }
+ return () => {
+ scaner.stop();
+ };
+ }, [elementRef.current, html5Qrcode, props.isOpen]);
+
+ return (
+ {
+ import("html5-qrcode").then((m) => setHtml5Qrcode(() => m.Html5Qrcode));
+ }}
+ onDidDismiss={() => props.onDismiss()}
+ >
+
+
+ props.onDismiss()}
+ >
+
+
+
+
+
+
+ );
+};
+
+const InputAssetWithDropDown = (props: {
+ assets: IAsset[];
+ inputFromAmount: number;
+ setInputFromAmount: Dispatch>;
+ setInputFromAsset: Dispatch>;
+}) => {
+ const { assets, setInputFromAmount, inputFromAmount, setInputFromAsset } =
+ props;
+ const [errorMessage, setErrorMessage] = useState();
+ const [selectedAsset, setSelectedAsset] = useState(assets[0]);
+ const [isLoading, setIsLoading] = useState(false);
+ const [popoverOpen, setPopoverOpen] = useState(false);
+ // const popover = useRef(null);
+
+ useEffect(() => {
+ if (selectedAsset) {
+ setInputFromAsset(selectedAsset);
+ }
+ return () => {};
+ });
+
+ const maxBalance = useMemo(() => {
+ // round to the lower tenth
+ return Math.floor(selectedAsset?.balance * 10000) / 10000;
+ }, [selectedAsset]);
+
+ const handleInputChange = async (
+ e: IonInputCustomEvent
+ ) => {
+ let value = Number((e.target as any).value || 0);
+ if (maxBalance && value > maxBalance) {
+ (e.target as any).value = maxBalance;
+ value = maxBalance;
+ }
+ if (value <= 0) {
+ setErrorMessage(() => undefined);
+ // UI loader control
+ setIsLoading(() => false);
+ return;
+ }
+ setInputFromAmount(() => value);
+ setErrorMessage(() => undefined);
+ // UI loader control
+ setIsLoading(() => false);
+ };
+
+ return (
+ <>
+
+
+
+ {
+ $event.stopPropagation();
+ // set position
+ // popover.current!.event = $event;
+ // open popover
+ setPopoverOpen(() => true);
+ }}
+ >
+
+
+
+
+
+
+
+ {selectedAsset?.symbol}
+
+ {
+ $event.stopPropagation();
+ setInputFromAmount(() => selectedAsset?.balance || 0);
+ }}
+ >
+ Max: {maxBalance}
+
+
+
+
+
+
+ {
+ if (isNumberKey(e)) {
+ setIsLoading(() => true);
+ }
+ }}
+ onIonInput={(e) => handleInputChange(e)}
+ />
+
+
+
+
+
+ setPopoverOpen(false)}
+ className="modalAlert"
+ >
+
+
+ Available assets
+
+
+
+ {assets
+ .filter((a) => a.balance > 0)
+ .map((asset, index) => (
+ {
+ setPopoverOpen(() => false);
+ setSelectedAsset(asset);
+ setInputFromAsset(asset);
+ setInputFromAmount(() => 0);
+ setErrorMessage(() => undefined);
+ // setQuote(() => undefined);
+ console.log({ selectedAsset });
+ }}
+ >
+
+
+
+
+ {asset.symbol}
+
+
+
+ {
+ CHAIN_AVAILABLES.find((c) => c.id === asset?.chain?.id)
+ ?.name
+ }
+
+
+
+
+ {Number(asset?.balance).toFixed(6)}
+
+
+
+ {getReadableAmount(
+ +asset?.balance,
+ Number(asset?.priceUsd),
+ "No deposit"
+ )}
+
+
+
+
+ ))}
+
+
+ >
+ );
+};
+
+export const TransferContainer = (props: { dismiss: () => Promise }) => {
+ const {
+ walletAddress,
+ isMagicWallet,
+ assets,
+ loadAssets,
+ transfer,
+ switchNetwork,
+ currentNetwork,
+ } = Store.useState(getWeb3State);
+ const [inputFromAmount, setInputFromAmount] = useState(0);
+ const [inputToAddress, setInputToAddress] = useState(
+ undefined
+ );
+ const [inputFromAsset, setInputFromAsset] = useState(
+ undefined
+ );
+ const [isScanModalOpen, setIsScanModalOpen] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+
+ const isValid =
+ inputFromAmount > 0 &&
+ inputToAddress &&
+ inputToAddress.length > 0 &&
+ inputFromAsset?.contractAddress;
+
+ const handleSend = async () => {
+ console.log("handleSend: ", {
+ inputFromAmount,
+ inputToAddress,
+ inputFromAsset,
+ });
+ if (inputFromAmount && inputToAddress && inputFromAsset?.contractAddress) {
+ if (
+ inputFromAsset?.chain?.id &&
+ inputFromAsset?.chain?.id !== currentNetwork
+ ) {
+ await switchNetwork(inputFromAsset?.chain?.id);
+ }
+ await transfer({
+ inputFromAmount,
+ inputToAddress,
+ inputFromAsset: inputFromAsset.contractAddress,
+ });
+ // finalize with reload asset list
+ await loadAssets(true);
+ }
+ };
+ return (
+ <>
+
+
+
+ Send token
+
+
+ {
+ props.dismiss();
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ Currently only support native token transfer
+
+
+
+
+
+ Token
+
+ a.type === "NATIVE")}
+ inputFromAmount={inputFromAmount}
+ setInputFromAmount={setInputFromAmount}
+ setInputFromAsset={setInputFromAsset}
+ />
+
+
+
+ Destination address
+
+
+
+
+ {
+ setInputToAddress(
+ () => $event.detail.value || undefined
+ );
+ }}
+ />
+
+
+ {
+ setIsScanModalOpen(() => true);
+ }}
+ >
+
+
+
+
+
+ {
+ if (data) {
+ setInputToAddress(() => data);
+ }
+ setIsScanModalOpen(() => false);
+ }}
+ />
+
+
+ {
+ setIsLoading(true);
+ await handleSend().catch((err: any) => err);
+ setIsLoading(false);
+ }}
+ >
+ Send
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/containers/desktop/AboutContainer.tsx b/src/containers/desktop/AboutContainer.tsx
new file mode 100644
index 00000000..37ed7431
--- /dev/null
+++ b/src/containers/desktop/AboutContainer.tsx
@@ -0,0 +1,282 @@
+import { getContributors } from "@/servcies/github.service";
+import {
+ IonAvatar,
+ IonBackButton,
+ IonButton,
+ IonButtons,
+ IonCol,
+ IonContent,
+ IonGrid,
+ IonHeader,
+ IonIcon,
+ IonPage,
+ IonRow,
+ IonText,
+ IonToolbar,
+} from "@ionic/react";
+import { logoGithub, logoTwitter } from "ionicons/icons";
+import { useEffect, useState } from "react";
+
+type Team = {
+ avatar: string;
+ name: string;
+ subStatus: string;
+ post: string;
+ links: {
+ icon: string;
+ url: string;
+ }[];
+};
+
+export default function AboutContainer() {
+ const [teams, setTeam] = useState([]);
+
+ const mainTeams: Team[] = [
+ {
+ avatar: "./assets/images/0xFazio.jpeg",
+ name: "FazioNico",
+ subStatus: "Founder",
+ post: "Chief Executive Officer",
+ links: [
+ {
+ icon: logoTwitter,
+ url: "https://x.com/0xFazio",
+ },
+ ],
+ },
+ ];
+
+ useEffect(() => {
+ getContributors("hexaonelabs", "hexa-lite")
+ .then((contributors) => {
+ return contributors.map((c) => {
+ const main = mainTeams.find(
+ (t) => c.login.toLowerCase() === t.name.toLowerCase()
+ );
+
+ return main
+ ? {
+ avatar: c.avatar_url,
+ subStatus: main.subStatus,
+ post: main.post,
+ links: [
+ ...main.links,
+ {
+ icon: logoGithub,
+ url: c.html_url,
+ },
+ ],
+ name: main.name,
+ }
+ : ({
+ avatar: c.avatar_url,
+ name: c.login,
+ subStatus: "Github contributor",
+ post: "Developper",
+ links: [
+ {
+ icon: logoGithub,
+ url: c.html_url,
+ },
+ ],
+ } as Team);
+ });
+ })
+ .then((teamData) => {
+ setTeam(() => teamData);
+ });
+ return () => setTeam([]);
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+ Build by the community, for the community
+
+
+
+
+ Hexa Lite project is starting by the main team of{" "}
+
+
+ Hexa One Labs
+
+
+ , a Swiss-based organization focused on innovation in the
+ decentralized finance sector. Our mission is straightforward:
+ to make DeFi accessible to everyone by developing products and
+ services that reduce friction and growth web3 adoption
+
+
+
+
+
+
+
+
+
+ Meet the main team
+
+
+ {teams.map((t, index) => (
+
+
+
+
+
+
+ {t.name}
+
+ {t.subStatus}
+
+
+
+ {t.post}
+
+ {t.links.map((l, i) => (
+ window.open(l.url, "_blank")}
+ >
+
+
+ ))}
+
+ ))}
+
+
+ window.open(
+ "https://github.com/hexaonelabs/hexa-lite/pulls",
+ "_blanck"
+ )
+ }
+ >
+ Become a contributor
+
+
+
+ by opening a pull request
+
+
+
+
+
+
+
+
+
+ Be a part of Gouvernance
+
+
+
+ To participate in governance using Snapshot, follow
+ these simple steps
+
+
+
+
+
+
+ Visit the{" "}
+
+
+ Snapshot
+
+ {" "}
+ platform
+
+ Connect your wallet
+ Browse through the listed proposals
+
+ Cast your vote on the proposals that align with your
+ vision
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/containers/desktop/AvailablePlatformsContainer.tsx b/src/containers/desktop/AvailablePlatformsContainer.tsx
new file mode 100644
index 00000000..84a66283
--- /dev/null
+++ b/src/containers/desktop/AvailablePlatformsContainer.tsx
@@ -0,0 +1,86 @@
+import { InstallPWASteps } from "@/components/ui/InstallPWASteps";
+import { IonAvatar, IonBackButton, IonButton, IonButtons, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonModal, IonPage, IonRow, IonText, IonToolbar, isPlatform } from "@ionic/react";
+import { logoTwitter } from "ionicons/icons";
+import { useState } from "react";
+
+export default function AvailablePlatformsContainer() {
+ const [isInstallModalOpen, setIsInstallModalOpen] = useState(false);
+
+ const teams = [
+ {
+ avatar: '',
+ name: 'Fazio Nicolas',
+ sumbStatus: 'Founder',
+ post: 'Chief Executive Officer',
+ links: [
+ {
+ icon: logoTwitter,
+ url: './assets/images/0xFazio.jpeg'
+ }
+ ]
+ }
+ ]
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Available on Web, iOS, Android and Desktop
+
+
+
+
+ Access Hexa Lite seamlessly across all your devices. Whether you're on the go with your smartphone, at your desk with your desktop computer, or relaxing at home with your tablet, our platform is available on iOS, Android, and Desktop, ensuring you never miss a beat in managing your decentralized finances
+
+
+
+
+ setIsInstallModalOpen(true)}>
+ iOS & Android install
+
+ window.open('https://github.com/hexaonelabs/hexa-lite', '_blanck')}
+ className="ion-padding-horizontal"
+ color="gradient">
+ Desktop download
+
+
+
+
+
+
+
+ setIsInstallModalOpen(false)}
+ className="modalAlert"
+ >
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/containers/DefiContainer.tsx b/src/containers/desktop/DefiContainer.tsx
similarity index 94%
rename from src/containers/DefiContainer.tsx
rename to src/containers/desktop/DefiContainer.tsx
index 2e265ea6..d2e008d8 100644
--- a/src/containers/DefiContainer.tsx
+++ b/src/containers/desktop/DefiContainer.tsx
@@ -17,28 +17,28 @@ import {
import { chevronDownOutline } from "ionicons/icons";
import { ChainId } from "@aave/contract-helpers";
-import { getPercent } from "../utils/utils";
-import { CHAIN_AVAILABLES } from "../constants/chains";
+import { getPercent } from "../../utils/utils";
+import { CHAIN_AVAILABLES } from "../../constants/chains";
import { useEffect, useState } from "react";
-import { MarketList } from "../components/MarketsList";
-import { currencyFormat } from "../utils/currency-format";
+import { MarketList } from "../../components/MarketsList";
import { valueToBigNumber } from "@aave/math-utils";
import { getReadableValue } from "@/utils/getReadableValue";
import Store from "@/store";
import { getPoolGroupsState, getProtocolSummaryState, getUserSummaryAndIncentivesGroupState, getWeb3State } from "@/store/selectors";
import { initializePools, initializeUserSummary } from "@/store/effects/pools.effect";
import { patchPoolsState } from "@/store/actions";
+import { currencyFormat } from "@/utils/currencyFormat";
export const minBaseTokenRemainingByNetwork: Record = {
[ChainId.optimism]: "0.0001",
[ChainId.arbitrum_one]: "0.0001",
};
-export const DefiContainer = ({
+export default function DefiContainer({
handleSegmentChange,
}: {
handleSegmentChange: (e: { detail: { value: string } }) => void;
-}) => {
+}) {
const { walletAddress } = Store.useState(getWeb3State);
const userSummaryAndIncentivesGroup = Store.useState(getUserSummaryAndIncentivesGroupState);
const poolGroups = Store.useState(getPoolGroupsState);
@@ -47,6 +47,8 @@ export const DefiContainer = ({
null
);
+ console.log("userSummaryAndIncentivesGroup>> ", userSummaryAndIncentivesGroup)
+
const totalBorrowsUsd = protocolSummary.reduce((prev, current)=> {
return prev + current.totalBorrowsUSD;
}, 0);
@@ -181,7 +183,7 @@ export const DefiContainer = ({
size-md="4"
class=" ion-padding-vertical"
>
- {currencyFormat(totalSupplyUsd)}
+ {currencyFormat.format(totalSupplyUsd)}
DEPOSIT BALANCE
@@ -203,8 +205,8 @@ export const DefiContainer = ({
- {currencyFormat(+totalBorrowsUsd)} of{" "}
- {currencyFormat(totalBorrowableUsd)}
+ {currencyFormat.format(+totalBorrowsUsd)} of{" "}
+ {currencyFormat.format(totalBorrowableUsd)}
@@ -214,7 +216,7 @@ export const DefiContainer = ({
size-md="4"
class=" ion-padding-vertical"
>
- {currencyFormat(totalAbailableToBorrow)}
+ {currencyFormat.format(totalAbailableToBorrow)}
AVAILABLE TO BORROW
@@ -372,11 +374,11 @@ export const DefiContainer = ({
class="ion-padding-horizontal ion-text-end ion-hide-md-down"
>
- {currencyFormat(+summary.totalSupplyUSD)}
+ {currencyFormat.format(+summary.totalSupplyUSD)}
- {currencyFormat(
+ {currencyFormat.format(
summary.totalSupplyUSD *
summary.currentLiquidationThreshold
)}{" "}
@@ -391,7 +393,7 @@ export const DefiContainer = ({
class="ion-padding-horizontal ion-text-end ion-hide-md-down"
>
- {currencyFormat(+summary.totalBorrowsUSD)}
+ {currencyFormat.format(+summary.totalBorrowsUSD)}
- {currencyFormat(
+ {currencyFormat.format(
(summary.totalCollateralUSD *
summary.currentLiquidationThreshold) -
summary.totalBorrowsUSD
@@ -413,8 +415,8 @@ export const DefiContainer = ({
class="ion-padding-horizontal ion-text-end"
>
- {currencyFormat(+summary.totalBorrowsUSD)} of{" "}
- {currencyFormat(
+ {currencyFormat.format(+summary.totalBorrowsUSD)} of{" "}
+ {currencyFormat.format(
summary.totalCollateralUSD *
summary.currentLiquidationThreshold
)}{" "}
diff --git a/src/containers/EarnContainer.tsx b/src/containers/desktop/EarnContainer.tsx
similarity index 94%
rename from src/containers/EarnContainer.tsx
rename to src/containers/desktop/EarnContainer.tsx
index 6efbcd4b..a7560a56 100644
--- a/src/containers/EarnContainer.tsx
+++ b/src/containers/desktop/EarnContainer.tsx
@@ -1,6 +1,6 @@
import { IonCol, IonGrid, IonRow, IonText } from "@ionic/react";
-import { ETHLiquidStakingstrategyCard } from "../components/ETHLiquidStakingstrategy";
-import { MATICLiquidStakingstrategyCard } from "../components/MATICLiquidStakingstrategy";
+import { ETHLiquidStakingstrategyCard } from "../../components/ETHLiquidStakingstrategy";
+import { MATICLiquidStakingstrategyCard } from "../../components/MATICLiquidStakingstrategy";
import { ATOMLiquidStakingstrategyCard } from "@/components/ATOMLiquidStakingstrategy";
import { ETHsfrxLiquidStakingstrategyCard } from "@/components/ETHsfrxLiquidStakingstrategy";
import { MoreInfo } from "@/components/MoreInfo";
@@ -31,7 +31,7 @@ const LSTInfo = () => {
);
}
-export function EarnContainer() {
+export default function EarnContainer() {
return (
diff --git a/src/containers/FiatContainer.tsx b/src/containers/desktop/FiatContainer.tsx
similarity index 100%
rename from src/containers/FiatContainer.tsx
rename to src/containers/desktop/FiatContainer.tsx
diff --git a/src/containers/LeaderboardContainer.tsx b/src/containers/desktop/LeaderboardContainer.tsx
similarity index 94%
rename from src/containers/LeaderboardContainer.tsx
rename to src/containers/desktop/LeaderboardContainer.tsx
index 20bc57aa..880a0a17 100644
--- a/src/containers/LeaderboardContainer.tsx
+++ b/src/containers/desktop/LeaderboardContainer.tsx
@@ -19,8 +19,9 @@ import {
import { FooterComponent } from "@/components/FooterComponent";
import { getReadableValue } from "@/utils/getReadableValue";
import { getLeaderboardDatas } from "@/servcies/datas.service";
+import { AppLogo } from "@/components/ui/AppLogo";
-export const Leaderboard: React.FC = () => {
+export default function Leaderboard() {
const [datas, setDatas] = React.useState<{address: string; totalPoints: string;}[]|null>(null);
useEffect(() => {
getLeaderboardDatas()
@@ -56,14 +57,7 @@ export const Leaderboard: React.FC = () => {
class="ion-text-center ion-margin-top ion-padding-top"
>
-
+
diff --git a/src/containers/SwapContainer.tsx b/src/containers/desktop/SwapContainer.tsx
similarity index 95%
rename from src/containers/SwapContainer.tsx
rename to src/containers/desktop/SwapContainer.tsx
index a46783c1..e22f94ad 100644
--- a/src/containers/SwapContainer.tsx
+++ b/src/containers/desktop/SwapContainer.tsx
@@ -14,11 +14,11 @@ import {
} from "@lifi/widget";
import type { Route } from "@lifi/sdk";
import { useEffect } from "react";
-import { useLoader } from "../context/LoaderContext";
-import { CHAIN_AVAILABLES, CHAIN_DEFAULT, NETWORK } from "../constants/chains";
+import { useLoader } from "../../context/LoaderContext";
+import { CHAIN_AVAILABLES, CHAIN_DEFAULT, NETWORK } from "../../constants/chains";
import { ethers } from "ethers";
-import { LiFiWidgetDynamic } from "../components/LiFiWidgetDynamic";
-import { LIFI_CONFIG } from "../servcies/lifi.service";
+import { LiFiWidgetDynamic } from "../../components/LiFiWidgetDynamic";
+import { LIFI_CONFIG } from "../../servcies/lifi.service";
// import { SquidWidgetDynamic } from "@/components/SquidWidgetDynamic";
import { SquidWidget } from "@0xsquid/widget";
import { SQUID_CONFIG } from "@/servcies/squid.service";
@@ -26,7 +26,7 @@ import { PointsData, addAddressPoints } from "@/servcies/datas.service";
import Store from "@/store";
import { getWeb3State } from "@/store/selectors";
-export function SwapContainer() {
+export default function SwapContainer() {
const {
web3Provider,
currentNetwork,
@@ -105,7 +105,7 @@ export function SwapContainer() {
// load environment config
const widgetConfig: WidgetConfig = {
...LIFI_CONFIG,
- fee: 0, // set fee to 0 for main swap feature
+ // fee: 0, // set fee to 0 for main swap feature
walletManagement: {
connect: async () => {
try {
diff --git a/src/containers/desktop/TokenDetailDesktopContainer.tsx b/src/containers/desktop/TokenDetailDesktopContainer.tsx
new file mode 100644
index 00000000..d6468edb
--- /dev/null
+++ b/src/containers/desktop/TokenDetailDesktopContainer.tsx
@@ -0,0 +1,396 @@
+import { IAsset } from "@/interfaces/asset.interface";
+import { getAssetIconUrl } from "@/utils/getAssetIconUrl";
+import {
+ IonAccordion,
+ IonAccordionGroup,
+ IonAvatar,
+ IonBadge,
+ IonButton,
+ IonChip,
+ IonCol,
+ IonContent,
+ IonFooter,
+ IonGrid,
+ IonHeader,
+ IonIcon,
+ IonItem,
+ IonLabel,
+ IonList,
+ IonListHeader,
+ IonModal,
+ IonNote,
+ IonRow,
+ IonSelect,
+ IonSelectOption,
+ IonSpinner,
+ IonText,
+ IonTitle,
+ IonToolbar,
+} from "@ionic/react";
+import { MobileActionNavButtons } from "../../components/mobile/ActionNavButtons";
+import { Suspense, lazy, useEffect, useState } from "react";
+import { ethers } from "ethers";
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import { CHAIN_AVAILABLES } from "@/constants/chains";
+import { airplane, chevronDown, close, closeSharp, download, paperPlane, repeat } from "ionicons/icons";
+import { DataItem } from "@/components/ui/LightChart";
+import { getTokenHistoryPrice } from "@/utils/getTokenHistoryPrice";
+import { TokenInfo, getTokenInfo } from "@/utils/getTokenInfo";
+import { numberFormat } from "@/utils/numberFormat";
+import { currencyFormat } from "@/utils/currencyFormat";
+import { TokenDetailDescription } from "@/components/ui/TokenDetailDescription";
+import { TokenDetailMarketDetail } from "@/components/ui/TokenDetailMarketData";
+import { isStableAsset } from "@/utils/isStableAsset";
+import { Currency } from "@/components/ui/Currency";
+
+const LightChart = lazy(() => import("@/components/ui/LightChart"));
+
+const getTxsFromAddress = async (address: string) => {
+ let provider = new ethers.providers.EtherscanProvider();
+ let history = await provider.getHistory(address);
+ console.log(history);
+};
+
+export const TokenDetailDesktopContainer = (props: {
+ data: {
+ name: string;
+ symbol: string;
+ priceUsd: number;
+ balance: number;
+ balanceUsd: number;
+ thumbnail: string;
+ assets: IAsset[];
+ };
+ dismiss: () => void;
+ setState: (state: any) => void;
+}) => {
+ const { data, dismiss } = props;
+ const { walletAddress } = Store.useState(getWeb3State);
+ const [dataChartHistory, setDataChartHistory] = useState([]);
+ const [tokenInfo, setTokenInfo] = useState(undefined);
+ const [isInfoOpen, setInfoOpen] = useState(false);
+
+ useEffect(() => {
+ if (!walletAddress) return;
+ getTxsFromAddress(walletAddress);
+ getTokenHistoryPrice(props.data.symbol).then((prices) => {
+ const data: DataItem[] = prices.map(([time, value]: string[]) => {
+ const dataItem = {
+ time: new Date(time).toISOString().split("T").shift() || "",
+ value: Number(value),
+ };
+ return dataItem;
+ });
+ setDataChartHistory(() => data.slice(0, data.length - 1));
+ });
+ getTokenInfo(props.data.symbol).then((tokenInfo) =>
+ setTokenInfo(() => tokenInfo)
+ );
+ }, [walletAddress]);
+
+ return (
+ <>
+
+
+
+ {data.symbol}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ (
+ event.target as any
+ ).src = `https://images.placeholders.dev/?width=42&height=42&text=${data.symbol}&bgColor=%23000000&textColor=%23182449`;
+ }}
+ />
+
+
+
+ {data.balance.toFixed(6)} {data.symbol}
+
+
+
+
+
+ { tokenInfo?.market_data?.price_change_percentage_24h_in_currency?.usd && (
+
+
+ ({tokenInfo.market_data.price_change_percentage_24h_in_currency.usd.toFixed(
+ 2
+ )}
+ % /24h )
+
+
+ )}
+
+
+ {isStableAsset(data.symbol) ? (
+ {
+ setInfoOpen(()=> true);
+ }} >
+ stable
+
+ ) : ''}
+
+
+
+ {
+ props.setState({ isTransferModalOpen: true });
+ }}>
+
+ Send
+
+ {
+ props.setState({ isDepositModalOpen: true });
+ }}>
+
+ Deposit
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Networks details
+
+
+ {data.assets
+ .sort((a, b) =>
+ a.chain && b.chain
+ ? a.chain.id - b.chain.id
+ : a.balance + b.balance
+ )
+ .map((token, index) => (
+
+
+ c.id === token.chain?.id
+ )?.logo
+ }
+ alt={token.symbol}
+ style={{ transform: "scale(1.01)" }}
+ onError={(event) => {
+ (
+ event.target as any
+ ).src = `https://images.placeholders.dev/?width=42&height=42&text=${token.symbol}&bgColor=%23000000&textColor=%23182449`;
+ }}
+ />
+
+
+ {token.chain?.name}
+
+
+ { numberFormat.format(token.balance)} {token.symbol}
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >}>
+
+
+ {props.data.symbol} / USD
+
+ 1 {data.symbol} = {currencyFormat.format(tokenInfo?.market_data?.current_price?.usd||data.priceUsd)}
+
+
+
+
+
+
+
+
+ {tokenInfo && (
+
+
+
+
+
+
+
+
+ )}
+
+
+
+
+
+ Market datas from Coingeeko API
+ Last update: {new Date(tokenInfo?.market_data?.last_updated||new Date ().toLocaleDateString()).toLocaleString()}
+
+
+
+
+
+
+
+
+ setInfoOpen(false)}
+ >
+
+
+
+
+
+ Informations
+
+
+
+
+ setInfoOpen(false) }
+ >
+
+
+
+
+
+
+
+ What is a stablecoin?
+
+
+
+
+ Stablecoins are a type of cryptocurrency whose value is pegged to another asset, such as a fiat currency or gold, to maintain a stable price.
+
+
+
+
+
+
+ They strive to provide an alternative to the high volatility of popular cryptocurrencies, making them potentially more suitable for common transactions.
+
+
+
+
+
+
+ Stablecoins can be utilized in various blockchain-based financial services and can even be used to pay for goods and services.
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/containers/desktop/WalletDesktopContainer.tsx b/src/containers/desktop/WalletDesktopContainer.tsx
new file mode 100644
index 00000000..02ec8382
--- /dev/null
+++ b/src/containers/desktop/WalletDesktopContainer.tsx
@@ -0,0 +1,365 @@
+import { card, download, eyeOffOutline, eyeOutline, paperPlane } from "ionicons/icons";
+import WalletBaseComponent, {
+ WalletComponentProps,
+} from "../../components/base/WalletBaseContainer";
+import {
+ IonButton,
+ IonCard,
+ IonCardContent,
+ IonCol,
+ IonGrid,
+ IonIcon,
+ IonLabel,
+ IonModal,
+ IonRow,
+ IonSearchbar,
+ IonText,
+} from "@ionic/react";
+import ConnectButton from "@/components/ConnectButton";
+import Store from "@/store";
+import { getAppSettings, getWeb3State } from "@/store/selectors";
+import { TokenDetailDesktopContainer } from "./TokenDetailDesktopContainer";
+import { currencyFormat } from "@/utils/currencyFormat";
+import { WalletAssetEntity } from "@/components/ui/WalletAssetEntity";
+import { Currency } from "@/components/ui/Currency";
+import { patchAppSettings } from "@/store/actions";
+import { ToggleHideCurrencyAmount } from "@/components/ui/ToggleHideCurrencyAmount";
+
+class WalletDesktopContainer extends WalletBaseComponent {
+ constructor(props: WalletComponentProps) {
+ super(props);
+ }
+
+ render() {
+
+ return (
+ <>
+ {super.render()}
+
+
+
+
+
+ Wallet
+
+
+
+
+
+
+
+
+
+
+ {/* send btn + Deposit btn */}
+ {
+ this.handleTransferClick(true);
+ }}
+ >
+
+ Send
+
+ {
+ this.handleDepositClick();
+ }}
+ >
+
+ Deposit
+
+
+
+
+ {!this.props.walletAddress && (
+
+
+
+
+ Connecting your wallet is key to accessing a snapshot of
+ your assets.
+ It grants you direct insight into your holdings and
+ balances.
+
+
+
+
+
+
+
+ )}
+
+ {this.props.walletAddress && this.state.assetGroup.length === 0 && (
+
+
+ {
+ super.handleBuyWithFiat(true);
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+ Buy crypto
+
+
+
+ You have to get ETH to use your wallet. Buy with
+ credit card or with Apple Pay
+
+
+
+ Buy crypto
+
+
+
+
+
+
+
+
+ {
+ super.handleDepositClick()
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+ Deposit assets
+
+
+
+ Transfer tokens from another wallet or from a
+ crypto exchange
+
+
+
+ Deposit assets
+
+
+
+
+
+
+
+
+
+
+ You are using a non-custodial wallet that give you complete
+ control over your cryptocurrency funds and private keys.
+ Unlike custodial wallets, you manage your own security,
+ enhancing privacy and independence in the decentralized
+ cryptocurrency space.
+
+
+
+
+ )}
+
+ {/* wrapper to display card with assets items */}
+ {this.state.assetGroup.length > 0 && (
+ <>
+
+
+ {
+ this.handleSearchChange(e);
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ Asset
+
+
+
+
+
+
+
+
+ Price
+
+
+
+
+ Balance
+
+
+
+
+ Value
+
+
+
+
+
+
+
+ {this.state.assetGroup
+ .filter((asset) =>
+ this.state.filterBy
+ ? asset.symbol
+ .toLowerCase()
+ .includes(this.state.filterBy.toLowerCase())
+ : true
+ )
+ .map((asset, index) => {
+ return (
+
+ this.handleTokenDetailClick(asset)
+ }
+ asset={asset}
+ key={index}
+ />
+ );
+ })}
+
+ {(this.state.assetGroup.filter((asset) =>
+ this.state.filterBy
+ ? asset.symbol
+ .toLowerCase()
+ .includes(this.state.filterBy.toLowerCase())
+ : true
+ ).length === 0) && (
+ Assets not found in your wallet
+ )}
+
+
+ >
+ )}
+
+
+ this.handleTokenDetailClick(null)}
+ className="modalPage"
+ >
+ {this.state.selectedTokenDetail && (
+ this.setState(state)}
+ data={this.state.selectedTokenDetail}
+ dismiss={() => this.handleTokenDetailClick(null)}
+ />
+ )}
+
+ >
+ );
+ }
+}
+
+const withStore = (Component: React.ComponentClass) => {
+ // use named function to prevent re-rendering failure
+ return function WalletDesktopContainerWithStore(props: {
+ // handleDepositClick: (state: boolean) => void;
+ }) {
+ const { walletAddress, assets, loadAssets } = Store.useState(getWeb3State);
+
+ return (
+ loadAssets(force)} />
+ );
+ };
+};
+export default withStore(WalletDesktopContainer);
diff --git a/src/containers/mobile/EarnMobileContainer.tsx b/src/containers/mobile/EarnMobileContainer.tsx
new file mode 100644
index 00000000..f0bd1352
--- /dev/null
+++ b/src/containers/mobile/EarnMobileContainer.tsx
@@ -0,0 +1,232 @@
+import {
+ IonAvatar,
+ IonButton,
+ IonButtons,
+ IonCol,
+ IonContent,
+ IonGrid,
+ IonHeader,
+ IonIcon,
+ IonItem,
+ IonLabel,
+ IonList,
+ IonRow,
+ IonSegment,
+ IonSegmentButton,
+ IonSkeletonText,
+ IonText,
+ IonToolbar,
+} from "@ionic/react";
+import { useEffect, useMemo, useState } from "react";
+import { MarketList } from "../../components/MarketsList";
+import {
+ initializePools,
+ initializeUserSummary,
+} from "@/store/effects/pools.effect";
+import Store from "@/store";
+import {
+ getPoolGroupsState,
+ getUserSummaryAndIncentivesGroupState,
+ getWeb3State,
+} from "@/store/selectors";
+import { patchPoolsState } from "@/store/actions";
+import { CHAIN_AVAILABLES } from "@/constants/chains";
+import { getReadableValue } from "@/utils/getReadableValue";
+import { ETHLiquidStakingstrategyCard } from "../../components/ETHLiquidStakingstrategy";
+import { MATICLiquidStakingstrategyCard } from "../../components/MATICLiquidStakingstrategy";
+import { close } from "ionicons/icons";
+
+export const EarnMobileContainer = (props: {
+ dismiss: ()=> Promise;
+}) => {
+ const [segment, setSegment] = useState("earn");
+ const { walletAddress } = Store.useState(getWeb3State);
+ const userSummaryAndIncentivesGroup = Store.useState(
+ getUserSummaryAndIncentivesGroupState
+ );
+ const poolGroups = Store.useState(getPoolGroupsState);
+
+ const totalTVL = useMemo(() => {
+ return poolGroups
+ .flatMap((g) => g.pools)
+ .reduce(
+ (prev, current) => prev + Number(current.totalLiquidityUSD || 0),
+ 0
+ );
+ }, [poolGroups]);
+
+ useEffect(() => {
+ if (poolGroups.length > 0 && totalTVL > 0) {
+ return;
+ }
+ initializePools();
+ }, []);
+
+ useEffect(() => {
+ if (!walletAddress) {
+ patchPoolsState({ userSummaryAndIncentivesGroup: null });
+ return;
+ }
+ if (!userSummaryAndIncentivesGroup && walletAddress) {
+ initializeUserSummary(walletAddress);
+ }
+ }, [walletAddress, userSummaryAndIncentivesGroup]);
+
+ return (
+ <>
+
+
+
+ setSegment(() => "earn")}
+ >
+ Earn
+
+ setSegment(() => "loan")}
+ >
+ Loan market
+
+
+
+ {
+ props.dismiss();
+ }}>
+
+
+
+
+
+
+ {segment === "loan" && (
+
+
+
+
+ Available Markets
+
+
+
+
+ Connect to DeFi liquidity protocols and access to{" "}
+ {poolGroups.length > 0 ? (
+ poolGroups.length
+ ) : (
+
+ )}{" "}
+ open markets across{" "}
+ {
+ CHAIN_AVAILABLES.filter(
+ (chain) =>
+ chain.type === "evm" || chain.type === "solana"
+ ).length
+ }{" "}
+ networks, borrow assets using your crypto as collateral
+ and earn interest without any restrictions or censorship
+ by providing liquidity over a
+
+
+ {}
+ {(totalTVL || 0) > 0 ? (
+ "$" + getReadableValue(totalTVL || 0)
+ ) : (
+
+ )}{" "}
+ TVL
+
+
+
+
+
+
+
+ {}}
+ />
+
+
+
+ )}
+
+ {segment === "earn" && (
+
+
+
+
+ Earn interest
+
+
+
+
+ Unlock the full potential of your assets by earning
+ intrest through Liquid Staking or Providing Liquidity to
+ the markets
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ >
+ );
+};
diff --git a/src/containers/mobile/SwapMobileContainer.tsx b/src/containers/mobile/SwapMobileContainer.tsx
new file mode 100644
index 00000000..a542dffe
--- /dev/null
+++ b/src/containers/mobile/SwapMobileContainer.tsx
@@ -0,0 +1,213 @@
+import {
+ IonButton,
+ IonButtons,
+ IonCol,
+ IonContent,
+ IonGrid,
+ IonHeader,
+ IonIcon,
+ IonRow,
+ IonText,
+ IonToolbar,
+ useIonToast,
+} from "@ionic/react";
+import {
+ HiddenUI,
+ RouteExecutionUpdate,
+ WidgetConfig,
+ WidgetEvent,
+ useWidgetEvents,
+} from "@lifi/widget";
+import type { Route } from "@lifi/sdk";
+import { useEffect } from "react";
+import { PointsData, addAddressPoints } from "@/servcies/datas.service";
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import { CHAIN_DEFAULT } from "@/constants/chains";
+import { ethers } from "ethers";
+import { LiFiWidgetDynamic } from "../../components/LiFiWidgetDynamic";
+import { useLoader } from "@/context/LoaderContext";
+import { LIFI_CONFIG } from "../../servcies/lifi.service";
+import { IAsset } from "@/interfaces/asset.interface";
+import { close } from "ionicons/icons";
+
+export const SwapMobileContainer = (props: {
+ token?: {
+ name: string;
+ symbol: string;
+ priceUsd: number;
+ balance: number;
+ balanceUsd: number;
+ thumbnail: string;
+ assets: IAsset[];
+ },
+ dismiss: ()=> void;
+}) => {
+ const {
+ web3Provider,
+ currentNetwork,
+ walletAddress,
+ connectWallet,
+ disconnectWallet,
+ switchNetwork,
+ loadAssets,
+ } = Store.useState(getWeb3State);
+ const widgetEvents = useWidgetEvents();
+ const { display: displayLoader, hide: hideLoader } = useLoader();
+ const toastContext = useIonToast();
+ const presentToast = toastContext[0];
+ const dismissToast = toastContext[1];
+
+ useEffect(() => {
+ const onRouteExecutionStarted = (route: Route) => {
+ console.log("[INFO] onRouteExecutionStarted fired.");
+ };
+ const onRouteExecutionCompleted = async (route: Route) => {
+ console.log("[INFO] onRouteExecutionCompleted fired.", route);
+ const data: PointsData = {
+ route,
+ actionType: "swap",
+ };
+ if (!walletAddress) {
+ return;
+ }
+ await addAddressPoints(walletAddress, data);
+ await loadAssets(true);
+ };
+ const onRouteExecutionFailed = (update: RouteExecutionUpdate) => {
+ console.log("[INFO] onRouteExecutionFailed fired.", update);
+ };
+
+ widgetEvents.on(WidgetEvent.RouteExecutionStarted, onRouteExecutionStarted);
+ widgetEvents.on(
+ WidgetEvent.RouteExecutionCompleted,
+ onRouteExecutionCompleted
+ );
+ widgetEvents.on(WidgetEvent.RouteExecutionFailed, onRouteExecutionFailed);
+
+ return () => widgetEvents.all.clear();
+ }, [widgetEvents]);
+
+ const signer =
+ web3Provider instanceof ethers.providers.Web3Provider && walletAddress
+ ? web3Provider?.getSigner()
+ : undefined;
+ // load environment config
+ const widgetConfig: WidgetConfig = {
+ ...LIFI_CONFIG,
+ containerStyle: {
+ border: `0px solid rgba(var(--ion-color-primary-rgb), 0.4);`,
+ },
+ hiddenUI: [
+ ...(LIFI_CONFIG?.hiddenUI as any[]),
+ HiddenUI.History,
+ HiddenUI.WalletMenu,
+ // HiddenUI.DrawerButton,
+ // HiddenUI.DrawerCloseButton
+ ],
+ fee: 0, // set fee to 0 for main swap feature
+ walletManagement: {
+ connect: async () => {
+ try {
+ await displayLoader();
+ await connectWallet();
+ if (!(web3Provider instanceof ethers.providers.Web3Provider)) {
+ throw new Error(
+ "[ERROR] Only support ethers.providers.Web3Provider"
+ );
+ }
+ const signer = web3Provider?.getSigner();
+ console.log("signer", signer);
+ if (!signer) {
+ throw new Error("Signer not found");
+ }
+ // return signer instance from JsonRpcSigner
+ hideLoader();
+ return signer;
+ } catch (error: any) {
+ // Log any errors that occur during the connection process
+ hideLoader();
+ await presentToast({
+ message: `[ERROR] Connect Failed with reason: ${
+ error?.message || error
+ }`,
+ color: "danger",
+ buttons: [
+ {
+ text: "x",
+ role: "cancel",
+ handler: () => {
+ dismissToast();
+ },
+ },
+ ],
+ });
+ throw new Error("handleConnect:" + error?.message);
+ }
+ },
+ disconnect: async () => {
+ try {
+ displayLoader();
+ await disconnectWallet();
+ hideLoader();
+ } catch (error: any) {
+ // Log any errors that occur during the disconnection process
+ console.log("handleDisconnect:", error);
+ hideLoader();
+ await presentToast({
+ message: `[ERROR] Disconnect Failed with reason: ${
+ error?.message || error
+ }`,
+ color: "danger",
+ buttons: [
+ {
+ text: "x",
+ role: "cancel",
+ handler: () => {
+ dismissToast();
+ },
+ },
+ ],
+ });
+ }
+ },
+ signer,
+ },
+ // set source chain to Polygon
+ fromChain: props?.token?.assets?.[0]?.chain?.id || CHAIN_DEFAULT.id,
+ // set destination chain to Optimism
+ toChain: currentNetwork || CHAIN_DEFAULT.id,
+ // set source token to ETH (Ethereum)
+ fromToken:
+ props?.token?.assets?.[0]?.contractAddress ||
+ "0x0000000000000000000000000000000000000000",
+ };
+
+ return (
+ <>
+
+
+
+ {
+ props.dismiss();
+ }}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/containers/mobile/TokenDetailMobileContainer.tsx b/src/containers/mobile/TokenDetailMobileContainer.tsx
new file mode 100644
index 00000000..7bc21111
--- /dev/null
+++ b/src/containers/mobile/TokenDetailMobileContainer.tsx
@@ -0,0 +1,425 @@
+import { IAsset } from "@/interfaces/asset.interface";
+import { getAssetIconUrl } from "@/utils/getAssetIconUrl";
+import {
+ IonAccordion,
+ IonAccordionGroup,
+ IonAvatar,
+ IonBadge,
+ IonButton,
+ IonButtons,
+ IonChip,
+ IonCol,
+ IonContent,
+ IonFooter,
+ IonGrid,
+ IonHeader,
+ IonIcon,
+ IonItem,
+ IonLabel,
+ IonList,
+ IonListHeader,
+ IonModal,
+ IonNote,
+ IonRow,
+ IonSelect,
+ IonSelectOption,
+ IonText,
+ IonTitle,
+ IonToolbar,
+} from "@ionic/react";
+import { MobileActionNavButtons } from "../../components/mobile/ActionNavButtons";
+import { Suspense, lazy, useEffect, useState } from "react";
+import { ethers } from "ethers";
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import { CHAIN_AVAILABLES } from "@/constants/chains";
+import { airplane, chevronDown, close, closeSharp, download, paperPlane } from "ionicons/icons";
+import { DataItem } from "@/components/ui/LightChart";
+import { getTokenHistoryPrice } from "@/utils/getTokenHistoryPrice";
+import { TokenInfo, getTokenInfo } from "@/utils/getTokenInfo";
+import { TokenDetailMarketDetail } from "@/components/ui/TokenDetailMarketData";
+import { TokenDetailDescription } from "@/components/ui/TokenDetailDescription";
+import { isStableAsset } from "@/utils/isStableAsset";
+import { Currency } from "@/components/ui/Currency";
+
+const LightChart = lazy(() => import("@/components/ui/LightChart"));
+
+const getTxsFromAddress = async (address: string) => {
+ let provider = new ethers.providers.EtherscanProvider();
+ let history = await provider.getHistory(address);
+ console.log(history);
+};
+
+export const TokenDetailMobileContainer = (props: {
+ data: {
+ name: string;
+ symbol: string;
+ priceUsd: number;
+ balance: number;
+ balanceUsd: number;
+ thumbnail: string;
+ assets: IAsset[];
+ };
+ dismiss: () => void;
+ setState: (state: any) => void;
+ setIsSwapModalOpen: (state: boolean) => void;
+}) => {
+ const { data, dismiss } = props;
+ const { walletAddress } = Store.useState(getWeb3State);
+ const [dataChartHistory, setDataChartHistory] = useState([]);
+ const [tokenInfo, setTokenInfo] = useState(undefined);
+ const [isInfoOpen, setInfoOpen] = useState(false);
+
+ useEffect(() => {
+ if (!walletAddress) return;
+ getTxsFromAddress(walletAddress);
+ getTokenHistoryPrice(props.data.symbol).then((prices) => {
+ const data: DataItem[] = prices.map(([time, value]: string[]) => {
+ const dataItem = {
+ time: new Date(time).toISOString().split("T").shift() || "",
+ value: Number(value),
+ };
+ return dataItem;
+ });
+ setDataChartHistory(() => data.slice(0, data.length - 1));
+ });
+ getTokenInfo(props.data.symbol).then((tokenInfo) =>
+ setTokenInfo(() => tokenInfo)
+ );
+ }, [walletAddress]);
+
+ return (
+ <>
+
+
+
+ {data.symbol}
+
+
+
+
+
+ {
+ props.dismiss();
+ }}>
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ (
+ event.target as any
+ ).src = `https://images.placeholders.dev/?width=42&height=42&text=${data.symbol}&bgColor=%23000000&textColor=%23182449`;
+ }}
+ />
+
+
+
+ {data.balance.toFixed(6)} {data.symbol}
+
+
+
+
+
+ { tokenInfo?.market_data?.price_change_percentage_24h_in_currency?.usd && (
+
+
+ ({tokenInfo.market_data.price_change_percentage_24h_in_currency.usd.toFixed(
+ 2
+ )}
+ % /24h )
+
+
+ )}
+
+ {isStableAsset(data.symbol) ? (
+ {
+ setInfoOpen(()=> true);
+ }}
+ style={{marginTop: '0.5rem'}}
+ color="success">
+ stable
+
+ ) : ''}
+
+
+
+
+
+
+
+
+
+
+
+
+ Networks details
+
+
+ {data.assets
+ .sort((a, b) =>
+ a.chain && b.chain
+ ? a.chain.id - b.chain.id
+ : a.balance + b.balance
+ )
+ .map((token, index) => (
+
+
+ c.id === token.chain?.id
+ )?.logo
+ }
+ alt={token.symbol}
+ style={{ transform: "scale(1.01)" }}
+ onError={(event) => {
+ (
+ event.target as any
+ ).src = `https://images.placeholders.dev/?width=42&height=42&text=${token.symbol}&bgColor=%23000000&textColor=%23182449`;
+ }}
+ />
+
+
+ {token.chain?.name}
+
+
+ {token.balance.toFixed(6)} {token.symbol}
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .....>}>
+
+
+
+ 1 {data.symbol} = $ {(tokenInfo?.market_data?.current_price?.usd||data.priceUsd).toFixed(2)}
+
+
+
+
+
+
+
+ {tokenInfo && (
+ <>
+
+
+
+
+
+
+ >
+ )}
+
+
+
+
+ Market datas from Coingeeko API
+ Last update: {new Date(tokenInfo?.market_data?.last_updated||new Date ().toLocaleDateString()).toLocaleString()}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ props.setState({ isTransferModalOpen: true });
+ }}
+ >
+
+ Send
+
+
+
+ {
+ props.setState({ isDepositModalOpen: true });
+ }}
+ >
+
+ Deposit
+
+
+
+
+
+ {/* props.setState(state)}
+ setIsSwapModalOpen={() => props.setIsSwapModalOpen(true)}
+ hideEarnBtn={true} /> */}
+
+
+
+ setInfoOpen(false)}
+ >
+
+
+
+
+
+ Informations
+
+
+
+
+ setInfoOpen(false) }
+ >
+
+
+
+
+
+
+
+ What is a stablecoin?
+
+
+
+
+ Stablecoins are a type of cryptocurrency whose value is pegged to another asset, such as a fiat currency or gold, to maintain a stable price.
+
+
+
+
+
+
+ They strive to provide an alternative to the high volatility of popular cryptocurrencies, making them potentially more suitable for common transactions.
+
+
+
+
+
+
+ Stablecoins can be utilized in various blockchain-based financial services and can even be used to pay for goods and services.
+
+
+
+
+
+
+
+ >
+ );
+};
diff --git a/src/containers/mobile/WalletMobileContainer.tsx b/src/containers/mobile/WalletMobileContainer.tsx
new file mode 100644
index 00000000..69a5d454
--- /dev/null
+++ b/src/containers/mobile/WalletMobileContainer.tsx
@@ -0,0 +1,507 @@
+import Store from "@/store";
+import WalletBaseComponent, {
+ SelectedTokenDetail,
+ WalletComponentProps,
+} from "../../components/base/WalletBaseContainer";
+import { getWeb3State } from "@/store/selectors";
+import { MobileActionNavButtons } from "@/components/mobile/ActionNavButtons";
+import { getMagic } from "@/servcies/magic";
+import { getAssetIconUrl } from "@/utils/getAssetIconUrl";
+import { getReadableValue } from "@/utils/getReadableValue";
+import {
+ IonPage,
+ IonHeader,
+ IonToolbar,
+ IonTitle,
+ IonContent,
+ IonGrid,
+ IonRow,
+ IonCol,
+ IonText,
+ IonSearchbar,
+ IonCard,
+ IonCardContent,
+ IonIcon,
+ IonList,
+ IonItemSliding,
+ IonItem,
+ IonAvatar,
+ IonLabel,
+ IonItemOptions,
+ IonItemOption,
+ IonAlert,
+ IonModal,
+ IonButton,
+ IonMenuToggle,
+ IonRefresher,
+ IonRefresherContent,
+ RefresherEventDetail,
+ IonChip,
+} from "@ionic/react";
+import { card, download, paperPlane, repeat, settings, settingsOutline } from "ionicons/icons";
+import { useState } from "react";
+import { IAsset } from "@/interfaces/asset.interface";
+import { SwapMobileContainer } from "@/containers/mobile/SwapMobileContainer";
+import { TokenDetailMobileContainer } from "@/containers/mobile/TokenDetailMobileContainer";
+import { EarnMobileContainer } from "@/containers/mobile/EarnMobileContainer";
+import { MenuSettings } from "@/components/ui/MenuSetting";
+import { currencyFormat } from "@/utils/currencyFormat";
+import { isStableAsset } from "@/utils/isStableAsset";
+import { Currency } from "@/components/ui/Currency";
+import { ToggleHideCurrencyAmount } from "@/components/ui/ToggleHideCurrencyAmount";
+
+interface WalletMobileComProps {
+ isMagicWallet: boolean;
+ isSwapModalOpen: SelectedTokenDetail | boolean | undefined;
+ setIsSwapModalOpen: (
+ value?: SelectedTokenDetail | boolean | undefined
+ ) => void;
+ isAlertOpen: boolean;
+ setIsAlertOpen: (value: boolean) => void;
+ isSettingOpen: boolean;
+ setIsSettingOpen: (value: boolean) => void;
+}
+
+class WalletMobileContainer extends WalletBaseComponent<
+ WalletComponentProps & WalletMobileComProps
+> {
+ constructor(props: WalletComponentProps & WalletMobileComProps) {
+ super(props);
+ }
+
+ async setIsSwapModalOpen(state?: SelectedTokenDetail | boolean | undefined) {
+ this.props.setIsSwapModalOpen(state);
+ }
+
+ async setIsSettingOpen(state: boolean ) {
+ this.props.setIsSettingOpen(state);
+ }
+
+ async handleDepositClick(state?: boolean | undefined) {
+ await super.handleDepositClick(state);
+ }
+
+ async handleTokenDetailClick(token?: any): Promise {
+ super.handleTokenDetailClick(token);
+ }
+
+ async handleEarnClick(): Promise {
+ super.handleEarnClick();
+ }
+
+ async handleTransferClick(state: boolean): Promise {
+ await super.handleTransferClick(state);
+ }
+
+ render() {
+ return (
+ <>
+
+
+
+
+ Wallet
+
+
+
+
+
+ this.setIsSettingOpen(true)}>
+
+
+
+
+
+
+
+ )=> {
+ await super.handleRefresh();
+ setTimeout(() => {
+ // Any calls to load data go here
+ $event.detail.complete();
+ }, 2000);
+ }}>
+
+
+
+
+
+
+
+
+
+
+
+ Wallet
+
+
+
+
+
+
+
+
+
+
+ this.setState(state)}
+ setIsSwapModalOpen={() =>
+ this.setIsSwapModalOpen(true)
+ }
+ />
+
+ {this.state.assetGroup.length > 0 && (
+
+
+
+ {
+ this.handleSearchChange(event);
+ }}
+ >
+
+
+
+ )}
+
+
+
+
+
+
+ {this.state.totalBalance <= 0 && (
+
+
+ super.handleBuyWithFiat(true)}>
+
+
+
+
+
+
+
+
+
+
+ Buy crypto
+
+
+
+ You have to get ETH to use your wallet. Buy
+ with credit card or with Apple Pay
+
+
+
+ Buy crypto
+
+
+
+
+
+
+
+ this.handleDepositClick()}
+ >
+
+
+
+
+
+
+
+
+
+
+ Deposit assets
+
+
+
+ Transefer tokens from another wallet or from a crypto exchange
+
+
+
+ Deposit assets
+
+
+
+
+
+
+
+
+ )}
+
+ {this.state.totalBalance > 0 && (
+
+
+
+ {this.state.assetGroup
+ .filter((asset) =>
+ this.state.filterBy
+ ? asset.symbol
+ .toLowerCase()
+ .includes(this.state.filterBy.toLowerCase())
+ : true
+ )
+ .sort((a, b) => (a.balanceUsd > b.balanceUsd ? -1 : 1))
+ .map((asset, index) => (
+
+ {
+ console.log("handleTokenDetailClick: ", asset);
+ this.handleTokenDetailClick(asset);
+ }}
+ >
+
+ {
+ (
+ event.target as any
+ ).src = `https://images.placeholders.dev/?width=42&height=42&text=${asset.symbol}&bgColor=%23000000&textColor=%23182449`;
+ }}
+ />
+
+
+
+
+ {asset.symbol}
+
+
+
+
+ {asset.name}
+
+
+
+ {isStableAsset(asset.symbol) ? (
+ stable
+ ) : ''}
+
+
+
+
+
+ {asset.balance.toFixed(6)}
+
+
+
+
+ {
+ // close the sliding item after clicking the option
+ (event.target as HTMLElement)
+ .closest("ion-item-sliding")
+ ?.close();
+ }}
+ >
+ {
+ this.handleTransferClick(true);
+ }}
+ >
+
+
+ {
+ this.setIsSwapModalOpen(asset);
+ }}
+ >
+
+
+
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+ You are using a non-custodial wallet that give you complete
+ control over your cryptocurrency funds and private keys.
+ Unlike custodial wallets, you manage your own security,
+ enhancing privacy and independence in the decentralized
+ cryptocurrency space.
+
+
+
+
+
+
+
+
+ this.setState({ isEarnModalOpen: false })}
+ >
+ this.setState({ isEarnModalOpen: false })} />
+
+
+ {
+ this.setIsSwapModalOpen(undefined);
+ }}
+ >
+ this.props.setIsSwapModalOpen(false)}
+ token={
+ typeof this.props.isSwapModalOpen !== "boolean"
+ ? this.props.isSwapModalOpen
+ : undefined
+ }
+ />
+
+
+ this.handleTokenDetailClick(null)}
+ >
+ {this.state.selectedTokenDetail && (
+ this.setState(state)}
+ setIsSwapModalOpen={() => this.setIsSwapModalOpen(true)}
+ data={this.state.selectedTokenDetail}
+ dismiss={() => this.handleTokenDetailClick(null)}
+ />
+ )}
+
+
+ this.setIsSettingOpen(false)}
+ >
+ this.setIsSettingOpen(false)} />
+
+
+ {super.render()}
+ >
+ );
+ }
+}
+
+const withStore = (
+ Component: React.ComponentClass
+) => {
+ // use named function to prevent re-rendering failure
+ return function WalletMobileContainerWithStore() {
+ const { walletAddress, assets, isMagicWallet, loadAssets } =
+ Store.useState(getWeb3State);
+ const [isSettingOpen, setIsSettingOpen] = useState(false);
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
+ const [isSwapModalOpen, setIsSwapModalOpen] = useState<
+ SelectedTokenDetail | boolean | undefined
+ >(undefined);
+
+ return (
+ setIsSwapModalOpen(value)}
+ isSettingOpen={isSettingOpen}
+ setIsSettingOpen={setIsSettingOpen}
+ modalOpts={{
+ initialBreakpoint: 1,
+ breakpoints: [0, 1],
+ }}
+ loadAssets={(force?: boolean)=> loadAssets(force)}
+ />
+ );
+ };
+};
+export default withStore(WalletMobileContainer);
diff --git a/src/containers/mobile/WelcomeMobileContainer.tsx b/src/containers/mobile/WelcomeMobileContainer.tsx
new file mode 100644
index 00000000..9da7a9a0
--- /dev/null
+++ b/src/containers/mobile/WelcomeMobileContainer.tsx
@@ -0,0 +1,46 @@
+import { IonCol, IonContent, IonGrid, IonImg, IonRow, IonText, useIonRouter } from "@ionic/react";
+import { IonPage, } from '@ionic/react';
+import ConnectButton from "../../components/ConnectButton";
+import Store from "@/store";
+import { getWeb3State } from "@/store/selectors";
+import { useEffect } from "react";
+import { AppLogo } from "@/components/ui/AppLogo";
+
+export default function WelcomeMobileContainer() {
+ const { walletAddress } = Store.useState(getWeb3State);
+ const router = useIonRouter();
+
+ useEffect(()=> {
+ if (walletAddress) {
+ router.push('wallet')
+ }
+ }, [walletAddress]);
+
+ return (
+
+
+
+
+
+
+
+ Hexa Lite
+
+ Build your wealth with cryptoassets
+
+
+ {!walletAddress && (
+
+ )}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/network/Bitcoin.ts b/src/network/Bitcoin.ts
index a0ed82c3..376ad220 100644
--- a/src/network/Bitcoin.ts
+++ b/src/network/Bitcoin.ts
@@ -34,4 +34,7 @@ export class BitcoinWalletUtils extends MagicWalletUtils {
throw new Error("loadBalances() - Method not implemented.");
}
+ async sendToken(destination: string, decimalAmount: number, contactAddress: string) {
+ throw new Error("sendToken() - Method not implemented.");
+ }
}
\ No newline at end of file
diff --git a/src/network/Cosmos.ts b/src/network/Cosmos.ts
index 6f43031a..ada8de3e 100644
--- a/src/network/Cosmos.ts
+++ b/src/network/Cosmos.ts
@@ -131,4 +131,8 @@ export class CosmosWalletUtils extends MagicWalletUtils {
async loadBalances() {
throw new Error("loadBalances() - Method not implemented.");
}
+
+ async sendToken(destination: string, decimalAmount: number, contactAddress: string) {
+ throw new Error("sendToken() - Method not implemented.");
+ }
}
\ No newline at end of file
diff --git a/src/network/EVM.ts b/src/network/EVM.ts
index 363aa58d..dabb8b1b 100644
--- a/src/network/EVM.ts
+++ b/src/network/EVM.ts
@@ -3,12 +3,34 @@ import { NETWORK } from "../constants/chains";
import { getTokensBalances } from "../servcies/ankr.service";
import { MagicWalletUtils } from "./MagicWallet";
import { getMagic } from "@/servcies/magic";
+import { getTokensPrice } from "@/servcies/lifi.service";
-const fetchUserAssets = async (walletAddress: string) => {
+/**
+ * Function tha takes wallet address and fetches all assets for that wallet
+ * using Ankr API. It also fetches token price from LiFi API if Ankr response contains
+ * token with balance > 0 && balanceUsd === 0 && priceUsd === 0
+ * This ensures that all tokens have price in USD and the total balance is calculated correctly
+ * for each token that user has in the wallet.
+ */
+const fetchUserAssets = async (walletAddress: string, force?: boolean) => {
console.log(`[INFO] fetchUserAssets()`, walletAddress);
if (!walletAddress) return null;
- const assets = await getTokensBalances([], walletAddress);
- return assets;
+ const assets = await getTokensBalances([], walletAddress, force);
+ // remove elements with 0 balance and add to new arrany using extracting
+ const assetWithBalanceUsd = [],
+ assetsWithoutBalanceUsd = [];
+ for (let i = 0; i < assets.length; i++) {
+ const asset = assets[i];
+ (asset.balanceUsd === 0 && asset.balance > 0)
+ ? assetsWithoutBalanceUsd.push(asset)
+ : assetWithBalanceUsd.push(asset);
+ }
+ // get token price for tokens without balanceUsd
+ const tokenWithbalanceUsd = await getTokensPrice(assetsWithoutBalanceUsd);
+ return [
+ ...assetWithBalanceUsd,
+ ...tokenWithbalanceUsd
+ ];
};
export class EVMWalletUtils extends MagicWalletUtils {
@@ -48,13 +70,62 @@ export class EVMWalletUtils extends MagicWalletUtils {
}
}
- async loadBalances() {
+ async loadBalances(force?: boolean) {
if (!this.walletAddress) return;
- const assets = await fetchUserAssets(this.walletAddress);
+ const assets = await fetchUserAssets(this.walletAddress, force);
if (!assets) return;
this.assets = assets;
}
+ async sendToken(destination: string, decimalAmount: number, contactAddress: string) {
+ if(!this.web3Provider) {
+ throw new Error("Web3Provider is not initialized");
+ }
+ try {
+ console.log({
+ destination, decimalAmount, contactAddress
+ })
+ const signer = this.web3Provider.getSigner();
+ const from = await signer.getAddress();
+ const amount = ethers.utils.parseUnits(decimalAmount.toString(), 18); // Convert 1 ether to wei
+ const contract = new ethers.Contract(contactAddress, ["function transfer(address, uint256)"], signer);
+
+ const data = contract.interface.encodeFunctionData("transfer", [destination, amount] );
+
+ const tx = await signer.sendTransaction({
+ to: destination,
+ value: amount,
+ // data
+ });
+ const receipt = await tx.wait();
+ // // Load token contract
+ // const tokenContract = new ethers.Contract(contactAddress, ['function transfer(address, uint256)'], signer);
+
+ // // Send tokens to recipient
+ // const transaction = await tokenContract.transfer(destination, amount);
+ // const receipt = await transaction.wait();
+ // console.log(receipt);
+
+
+
+ //Define the data parameter
+ // const data = contract.interface.encodeFunctionData("transfer", [destination, amount] )
+ // const tx = await signer.sendTransaction({
+ // to: contactAddress,
+ // from,
+ // value: ethers.utils.parseUnits("0.000", "ether"),
+ // data: data
+ // });
+ // // const tx = await contract.transfer(destination, amount);
+ // // Wait for transaction to be mined
+ // const receipt = await tx.wait();
+ return receipt;
+ } catch (err: any) {
+ console.error(err);
+ throw new Error("Error during sending token");
+ }
+ }
+
private async _setMetamaskNetwork() {
if (!this.web3Provider) {
throw new Error("Web3Provider is not initialized");
@@ -75,4 +146,14 @@ export class EVMWalletUtils extends MagicWalletUtils {
);
}
}
+
+ async estimateGas() {
+ // const limit = await provider.estimateGas({
+ // from: signer.address,
+ // to: tokenContract,
+ // value: ethers.utils.parseUnits("0.000", "ether"),
+ // data: data
+
+ // });
+ }
}
diff --git a/src/network/MagicWallet.ts b/src/network/MagicWallet.ts
index db6b1bed..c8384dd1 100644
--- a/src/network/MagicWallet.ts
+++ b/src/network/MagicWallet.ts
@@ -31,7 +31,8 @@ export abstract class MagicWalletUtils {
public abstract web3Provider: Web3ProviderType | null;
public abstract isMagicWallet: boolean;
- public abstract loadBalances(): Promise;
+ public abstract loadBalances(force?: boolean): Promise;
+ public abstract sendToken(destination: string, decimalAmount: number, contactAddress: string): Promise;
protected abstract _initializeWeb3(): Promise;
/**
diff --git a/src/network/Solana.ts b/src/network/Solana.ts
index ff0628a2..300f9f54 100644
--- a/src/network/Solana.ts
+++ b/src/network/Solana.ts
@@ -42,4 +42,8 @@ export class SolanaWalletUtils extends MagicWalletUtils {
async loadBalances() {
throw new Error("loadBalances() - Method not implemented.");
}
+
+ async sendToken(destination: string, decimalAmount: number, contactAddress: string) {
+ throw new Error("sendToken() - Method not implemented.");
+ }
}
\ No newline at end of file
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index b5fef242..915204f0 100755
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -21,6 +21,9 @@ import '../styles/global.scss';
import { AppProps } from 'next/app';
import { outfit } from '@/styles/fonts';
+import ErrorBoundary from '@/containers/ErrorBoundary';
+import Script from 'next/script';
+import { GoogleAnalytics } from '@next/third-parties/google'
function MyApp({ Component, pageProps }: AppProps) {
return (
@@ -58,11 +61,9 @@ function MyApp({ Component, pageProps }: AppProps) {
-
-
-
+
+
{/* */}
-
+
+
+
+
>
);
}
diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx
index b3290c8b..88195dd0 100644
--- a/src/pages/_document.tsx
+++ b/src/pages/_document.tsx
@@ -1,5 +1,5 @@
import { Html, Head, Main, NextScript } from 'next/document'
-
+
export default function Document() {
return (
diff --git a/src/servcies/ankr.service.ts b/src/servcies/ankr.service.ts
index a1e15b5f..955ef635 100644
--- a/src/servcies/ankr.service.ts
+++ b/src/servcies/ankr.service.ts
@@ -1,3 +1,4 @@
+import { IAsset } from "@/interfaces/asset.interface";
import { IChain, CHAIN_AVAILABLES } from "../constants/chains";
interface IAnkrTokenReponse {
@@ -15,7 +16,40 @@ interface IAnkrTokenReponse {
contractAddress?: string;
}
-const formatingTokensBalances = (assets: IAnkrTokenReponse[], address: string, chainsList: IChain[]) => {
+const fake_data = [
+ {
+ blockchain: "optimism",
+ tokenName: "Ether",
+ tokenSymbol: "ETH",
+ tokenDecimals: 18,
+ tokenType: "NATIVE",
+ holderAddress: "0x475ef9fb4f8d43b63ac9b22fa41fd4db8a103550",
+ balance: "0.001942681537656218",
+ balanceRawInteger: "1942681537656218",
+ balanceUsd: "7.097286526117426483",
+ tokenPrice: "3.282317948340073075",
+ thumbnail: "https://ankrscan.io/assets/blockchains/eth.svg",
+ },
+ {
+ blockchain: "optimism",
+ tokenName: "Aave Optimism wstETH",
+ tokenSymbol: "aOptwstETH",
+ tokenDecimals: 18,
+ tokenType: "ERC20",
+ contractAddress: "0xc45a479877e1e9dfe9fcd4056c699575a1045daa",
+ holderAddress: "0x475ef9fb4f8d43b63ac9b22fa41fd4db8a103550",
+ balance: "0.000408917159923751",
+ balanceRawInteger: "408917159923751",
+ balanceUsd: "0",
+ tokenPrice: "0",
+ thumbnail: "",
+ },
+];
+
+const formatingTokensBalances = (
+ assets: IAnkrTokenReponse[],
+ chainsList: IChain[]
+): IAsset[] => {
return assets.map((asset) => {
return {
chain: chainsList.find((c) => c.value === asset.blockchain),
@@ -30,12 +64,37 @@ const formatingTokensBalances = (assets: IAnkrTokenReponse[], address: string, c
balanceUsd: Number(asset.balanceUsd),
priceUsd: Number(asset.tokenPrice),
thumbnail: asset.thumbnail,
- contractAddress: asset.tokenType === 'NATIVE'
- ? '0x0000000000000000000000000000000000000000'
- : asset.contractAddress,
- }
+ contractAddress:
+ asset.tokenType === "NATIVE"
+ ? "0x0000000000000000000000000000000000000000"
+ : asset.contractAddress,
+ };
});
-}
+};
+
+const getCachedData = async (key: string, force?: boolean) => {
+ const data = localStorage.getItem(key);
+ if (!data) {
+ console.log("No data in cache.");
+ return null;
+ }
+ // check expiration cache using timestamp 10 minutes
+ const parsedData = JSON.parse(data);
+ if (Date.now() - parsedData.timestamp > 10 * 60 * 1000 && !force) {
+ console.log("Expire cache 10 minute");
+ return null;
+ }
+ if (Date.now() - parsedData.timestamp > 1 * 60 * 1000 && force) {
+ console.log("Expire cache 1 minute");
+ return null;
+ }
+ console.log("[INFO] {ankrFactory} data from cache: ", parsedData.data);
+ return parsedData.data;
+};
+
+const setCachedData = async (key: string, data: any) => {
+ localStorage.setItem(key, JSON.stringify({ data, timestamp: Date.now() }));
+};
/**
* Doc url: https://www.ankr.com/docs/app-chains/components/advanced-api/token-methods/#ankr_getaccountbalance
@@ -43,38 +102,55 @@ const formatingTokensBalances = (assets: IAnkrTokenReponse[], address: string, c
* @param address wallet address to get balances
* @returns object with balances property that contains an array of TokenInterface
*/
-export const getTokensBalances = async (chainIds: number[], address: string) => {
- const APP_ANKR_APIKEY = process.env.NEXT_PUBLIC_APP_ANKR_APIKEY;
+export const getTokensBalances = async (
+ chainIds: number[],
+ address: string,
+ force?: boolean
+): Promise => {
const chainsList =
chainIds.length <= 0
? CHAIN_AVAILABLES
: CHAIN_AVAILABLES.filter((availableChain) =>
chainIds.find((c) => c === availableChain.id)
);
+ // return fake_data for DEV mode
+ if (process.env.NEXT_PUBLIC_APP_IS_PROD === "false") {
+ console.log("[INFO] DEV mode return fake data");
+ const balances = formatingTokensBalances(fake_data, chainsList);
+ return balances;
+ }
+ const KEY = `hexa-ankr-service-${address}`;
+ const cachedData = await getCachedData(KEY, force);
+ console.log("cachedData:", cachedData);
+ if (cachedData) {
+ return cachedData;
+ }
+ const APP_ANKR_APIKEY = process.env.NEXT_PUBLIC_APP_ANKR_APIKEY;
const blockchain = chainsList
- .filter(({type})=> type === 'evm')
- .map(({value}) => value);
+ .filter(({ type }) => type === "evm")
+ .map(({ value }) => value);
const url = `https://rpc.ankr.com/multichain/${APP_ANKR_APIKEY}`;
const options: RequestInit = {
- method: 'POST',
+ method: "POST",
headers: {
- accept: 'application/json',
- 'content-type': 'application/json',
+ accept: "application/json",
+ "content-type": "application/json",
},
body: JSON.stringify({
- jsonrpc: '2.0',
- method: 'ankr_getAccountBalance',
+ jsonrpc: "2.0",
+ method: "ankr_getAccountBalance",
params: {
blockchain,
walletAddress: address,
- onlyWhitelisted: false,
+ onlyWhitelisted: true,
},
id: 1,
}),
};
const res = await fetch(url, options);
const assets = (await res.json())?.result?.assets;
- const balances = formatingTokensBalances(assets, address, chainsList);
- console.log('[INFO] {ankrFactory} getTokensBalances(): ', balances);
+ const balances = formatingTokensBalances(assets, chainsList);
+ console.log("[INFO] {ankrFactory} getTokensBalances(): ", balances);
+ await setCachedData(KEY, balances);
return balances;
-};
\ No newline at end of file
+};
diff --git a/src/servcies/datas.service.ts b/src/servcies/datas.service.ts
index 97a81b87..3efb51ae 100644
--- a/src/servcies/datas.service.ts
+++ b/src/servcies/datas.service.ts
@@ -169,7 +169,10 @@ export const addUTM = async (data: {[kex: string]: string}): Promise<{message: s
// Create a reference to the user's points in the Firebase database
const utmRef = ref(database, `utm`);
// Use push method to add the new task object inside the array
- await push(utmRef, data);
+ await push(utmRef, {
+ ...data,
+ createdAt: Date.now()
+ });
return {
message: "UTM added successfully 👍"
};
diff --git a/src/servcies/github.service.ts b/src/servcies/github.service.ts
new file mode 100644
index 00000000..ae367c54
--- /dev/null
+++ b/src/servcies/github.service.ts
@@ -0,0 +1,28 @@
+
+export interface Contributor {
+ login: string;
+ id: number;
+ node_id: string;
+ avatar_url: string;
+ gravatar_id: string;
+ url: string;
+ html_url: string;
+ followers_url: string;
+ following_url: string;
+ gists_url: string;
+ starred_url: string;
+ subscriptions_url: string;
+ organizations_url: string;
+ repos_url: string;
+ events_url: string;
+ received_events_url: string;
+ type: string;
+ site_admin: boolean;
+ contributions: number;
+}
+
+export const getContributors = async (owner: string, repository: string) => {
+ const response = await fetch(`https://api.github.com/repos/${owner}/${repository}/contributors`);
+ const data = await response.json();
+ return data as Contributor[];
+}
\ No newline at end of file
diff --git a/src/servcies/lifi.service.ts b/src/servcies/lifi.service.ts
index 13afe431..651f294d 100644
--- a/src/servcies/lifi.service.ts
+++ b/src/servcies/lifi.service.ts
@@ -1,74 +1,74 @@
-
+import { IAsset } from "@/interfaces/asset.interface";
import { HiddenUI, WidgetConfig } from "@lifi/widget";
import { ethers, Contract } from "ethers";
export interface LiFiQuoteResponse {
- type: string;
- id: string;
- tool: string;
- toolDetails: ToolDetails;
- action: Action;
- estimate: Estimate;
- includedSteps: IncludedStep[];
- integrator: string;
+ type: string;
+ id: string;
+ tool: string;
+ toolDetails: ToolDetails;
+ action: Action;
+ estimate: Estimate;
+ includedSteps: IncludedStep[];
+ integrator: string;
transactionRequest: TransactionRequest;
}
interface Action {
- fromToken: Token;
- fromAmount: string;
- toToken: Token;
- fromChainId: number;
- toChainId: number;
- slippage: number;
- fromAddress: string;
- toAddress: string;
+ fromToken: Token;
+ fromAmount: string;
+ toToken: Token;
+ fromChainId: number;
+ toChainId: number;
+ slippage: number;
+ fromAddress: string;
+ toAddress: string;
destinationGasConsumption?: string;
}
interface Token {
- address: string;
- chainId: number;
- symbol: string;
+ address: string;
+ chainId: number;
+ symbol: string;
decimals: number;
- name: string;
+ name: string;
priceUSD: string;
- logoURI: string;
- coinKey: string;
+ logoURI: string;
+ coinKey: string;
}
interface Estimate {
- tool: string;
- approvalAddress: string;
- toAmountMin: string;
- toAmount: string;
- fromAmount: string;
- feeCosts: FeeCost[];
- gasCosts: GasCost[];
+ tool: string;
+ approvalAddress: string;
+ toAmountMin: string;
+ toAmount: string;
+ fromAmount: string;
+ feeCosts: FeeCost[];
+ gasCosts: GasCost[];
executionDuration: number;
- fromAmountUSD?: string;
- toAmountUSD?: string;
- toolData?: ToolData;
+ fromAmountUSD?: string;
+ toAmountUSD?: string;
+ toolData?: ToolData;
}
interface FeeCost {
- name: string;
+ name: string;
description: string;
- percentage: string;
- token: Token;
- amount: string;
- amountUSD: string;
- included: boolean;
+ percentage: string;
+ token: Token;
+ amount: string;
+ amountUSD: string;
+ included: boolean;
}
interface GasCost {
- type: string;
- price: string;
- estimate: string;
- limit: string;
- amount: string;
+ type: string;
+ price: string;
+ estimate: string;
+ limit: string;
+ amount: string;
amountUSD: string;
- token: Token;
+ token: Token;
}
interface ToolData {
@@ -76,669 +76,701 @@ interface ToolData {
}
interface IncludedStep {
- id: string;
- type: string;
- action: Action;
- estimate: Estimate;
- tool: string;
+ id: string;
+ type: string;
+ action: Action;
+ estimate: Estimate;
+ tool: string;
toolDetails: ToolDetails;
}
interface ToolDetails {
- key: string;
- name: string;
+ key: string;
+ name: string;
logoURI: string;
}
interface TransactionRequest {
- data: string;
- to: string;
- value: string;
- from: string;
- chainId: number;
+ data: string;
+ to: string;
+ value: string;
+ from: string;
+ chainId: number;
gasPrice: string;
gasLimit: string;
}
const ERC20_ABI = [
{
- "name": "approve",
- "inputs": [
- {
- "internalType": "address",
- "name": "spender",
- "type": "address"
- },
- {
- "internalType": "uint256",
- "name": "amount",
- "type": "uint256"
- }
- ],
- "outputs": [
- {
- "internalType": "bool",
- "name": "",
- "type": "bool"
- }
- ],
- "stateMutability": "nonpayable",
- "type": "function"
+ name: "approve",
+ inputs: [
+ {
+ internalType: "address",
+ name: "spender",
+ type: "address",
+ },
+ {
+ internalType: "uint256",
+ name: "amount",
+ type: "uint256",
+ },
+ ],
+ outputs: [
+ {
+ internalType: "bool",
+ name: "",
+ type: "bool",
+ },
+ ],
+ stateMutability: "nonpayable",
+ type: "function",
},
{
- "name": "allowance",
- "inputs": [
- {
- "internalType": "address",
- "name": "owner",
- "type": "address"
- },
- {
- "internalType": "address",
- "name": "spender",
- "type": "address"
- }
- ],
- "outputs": [
- {
- "internalType": "uint256",
- "name": "",
- "type": "uint256"
- }
- ],
- "stateMutability": "view",
- "type": "function"
- }
+ name: "allowance",
+ inputs: [
+ {
+ internalType: "address",
+ name: "owner",
+ type: "address",
+ },
+ {
+ internalType: "address",
+ name: "spender",
+ type: "address",
+ },
+ ],
+ outputs: [
+ {
+ internalType: "uint256",
+ name: "",
+ type: "uint256",
+ },
+ ],
+ stateMutability: "view",
+ type: "function",
+ },
];
/**
* Requesting possibles connection
- * @param fromChain
- * @param toChain
- * @param fromToken
- * @param toToken
- * @param fromAmount
- * @param fromAddress
+ * @param fromChain
+ * @param toChain
+ * @param fromToken
+ * @param toToken
+ * @param fromAmount
+ * @param fromAddress
*/
export const getConnections = async (
- fromChain: string,
- toChain: string,
- fromToken: string,
- toToken: string,
- fromAmount: string,
- fromAddress: string,
+ fromChain: string,
+ toChain: string,
+ fromToken: string,
+ toToken: string,
+ fromAmount: string,
+ fromAddress: string
) => {
const integrator = "hexa-lite";
const fee = 0.005;
// build url params from input
const params = new URLSearchParams();
- params.append('fromChain', fromChain);
- params.append('toChain', toChain);
- params.append('fromToken', fromToken);
- params.append('toToken', toToken);
- params.append('fromAmount', fromAmount);
- params.append('fromAddress', fromAddress);
- params.append('integrator', integrator);
- params.append('fee', fee.toString());
- const reponse = await fetch(`https://li.quest/v1/connections?${params.toString()}`);
+ params.append("fromChain", fromChain);
+ params.append("toChain", toChain);
+ params.append("fromToken", fromToken);
+ params.append("toToken", toToken);
+ params.append("fromAmount", fromAmount);
+ params.append("fromAddress", fromAddress);
+ params.append("integrator", integrator);
+ params.append("fee", fee.toString());
+ const reponse = await fetch(
+ `https://li.quest/v1/connections?${params.toString()}`
+ );
const result = await reponse.json();
return result;
-}
+};
/**
* Requesting a Quote
- * @param fromChain
- * @param toChain
- * @param fromToken
- * @param toToken
- * @param fromAmount
- * @param fromAddress
- * @returns
+ * @param fromChain
+ * @param toChain
+ * @param fromToken
+ * @param toToken
+ * @param fromAmount
+ * @param fromAddress
+ * @returns
*/
export const getQuote = async (
- fromChain: string,
- toChain: string,
- fromToken: string,
- toToken: string,
- fromAmount: string,
- fromAddress: string,
- ): Promise => {
+ fromChain: string,
+ toChain: string,
+ fromToken: string,
+ toToken: string,
+ fromAmount: string,
+ fromAddress: string
+): Promise => {
const integrator = "hexa-lite";
const fee = 0.005;
// build url params from input
const params = new URLSearchParams();
- params.append('fromChain', fromChain);
- params.append('toChain', toChain);
- params.append('fromToken', fromToken);
- params.append('toToken', toToken);
- params.append('fromAmount', fromAmount);
- params.append('fromAddress', fromAddress);
- params.append('integrator', integrator);
- params.append('fee', fee.toString());
+ params.append("fromChain", fromChain);
+ params.append("toChain", toChain);
+ params.append("fromToken", fromToken);
+ params.append("toToken", toToken);
+ params.append("fromAmount", fromAmount);
+ params.append("fromAddress", fromAddress);
+ params.append("integrator", integrator);
+ params.append("fee", fee.toString());
const reponse = await fetch(`https://li.quest/v1/quote?${params.toString()}`);
- const result:LiFiQuoteResponse = await reponse.json()
+ const result: LiFiQuoteResponse = await reponse.json();
return result;
-}
+};
export const fakeQuote = {
- "type": "lifi",
- "id": "14c2de39-e6ce-4e9c-b438-bc68977912f4",
- "tool": "stargate",
- "toolDetails": {
- "key": "stargate",
- "name": "Stargate",
- "logoURI": "https://raw.githubusercontent.com/lifinance/types/5685c638772f533edad80fcb210b4bb89e30a50f/src/assets/icons/bridges/stargate.png"
+ type: "lifi",
+ id: "14c2de39-e6ce-4e9c-b438-bc68977912f4",
+ tool: "stargate",
+ toolDetails: {
+ key: "stargate",
+ name: "Stargate",
+ logoURI:
+ "https://raw.githubusercontent.com/lifinance/types/5685c638772f533edad80fcb210b4bb89e30a50f/src/assets/icons/bridges/stargate.png",
},
- "action": {
- "fromToken": {
- "address": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "chainId": 10,
- "symbol": "aOptUSDC",
- "decimals": 6,
- "name": "Aave Optimism USDC",
- "priceUSD": "1",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
- "coinKey": "aOptUSDC"
+ action: {
+ fromToken: {
+ address: "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ chainId: 10,
+ symbol: "aOptUSDC",
+ decimals: 6,
+ name: "Aave Optimism USDC",
+ priceUSD: "1",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
+ coinKey: "aOptUSDC",
},
- "fromAmount": "0.2",
- "toToken": {
- "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
- "symbol": "USDC",
- "decimals": 6,
- "chainId": 1,
- "name": "USD Coin",
- "coinKey": "USDC",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ fromAmount: "0.2",
+ toToken: {
+ address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
+ symbol: "USDC",
+ decimals: 6,
+ chainId: 1,
+ name: "USD Coin",
+ coinKey: "USDC",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "fromChainId": 10,
- "toChainId": 1,
- "slippage": 0.005,
- "fromAddress": "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7",
- "toAddress": "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7"
+ fromChainId: 10,
+ toChainId: 1,
+ slippage: 0.005,
+ fromAddress: "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7",
+ toAddress: "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7",
},
- "estimate": {
- "tool": "stargate",
- "approvalAddress": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
- "toAmountMin": "2",
- "toAmount": "2",
- "fromAmount": "2",
- "feeCosts": [
+ estimate: {
+ tool: "stargate",
+ approvalAddress: "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
+ toAmountMin: "2",
+ toAmount: "2",
+ fromAmount: "2",
+ feeCosts: [
{
- "name": "Integrator Fee",
- "description": "Fee shared between integrator and LI.FI",
- "token": {
- "address": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "chainId": 10,
- "symbol": "aOptUSDC",
- "decimals": 6,
- "name": "Aave Optimism USDC",
- "priceUSD": "1",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
- "coinKey": "aOptUSDC"
+ name: "Integrator Fee",
+ description: "Fee shared between integrator and LI.FI",
+ token: {
+ address: "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ chainId: 10,
+ symbol: "aOptUSDC",
+ decimals: 6,
+ name: "Aave Optimism USDC",
+ priceUSD: "1",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
+ coinKey: "aOptUSDC",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0",
- "included": true
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0",
+ included: true,
},
{
- "name": "LayerZero fees",
- "description": "Infrastructure fee paid in native token",
- "token": {
- "address": "0x0000000000000000000000000000000000000000",
- "symbol": "ETH",
- "decimals": 18,
- "chainId": 10,
- "name": "ETH",
- "coinKey": "ETH",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
- "priceUSD": "1573.41"
+ name: "LayerZero fees",
+ description: "Infrastructure fee paid in native token",
+ token: {
+ address: "0x0000000000000000000000000000000000000000",
+ symbol: "ETH",
+ decimals: 18,
+ chainId: 10,
+ name: "ETH",
+ coinKey: "ETH",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
+ priceUSD: "1573.41",
},
- "amount": "3358382275654522",
- "amountUSD": "5.28",
- "percentage": "2642056.1282",
- "included": false
+ amount: "3358382275654522",
+ amountUSD: "5.28",
+ percentage: "2642056.1282",
+ included: false,
},
{
- "name": "Equilibrium fees",
- "description": "The fee paid to users who rebalance tokens for the stargate protocol",
- "token": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ name: "Equilibrium fees",
+ description:
+ "The fee paid to users who rebalance tokens for the stargate protocol",
+ token: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0.0000",
- "included": true
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0.0000",
+ included: true,
},
{
- "name": "Liquidity provider fees",
- "description": "The fee paid to liquidity providers for the stargate protocol",
- "token": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ name: "Liquidity provider fees",
+ description:
+ "The fee paid to liquidity providers for the stargate protocol",
+ token: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0.0000",
- "included": true
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0.0000",
+ included: true,
},
{
- "name": "Protocol fees",
- "description": "The fee paid to the stargate protocol",
- "token": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ name: "Protocol fees",
+ description: "The fee paid to the stargate protocol",
+ token: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0.0000",
- "included": true
- }
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0.0000",
+ included: true,
+ },
],
- "gasCosts": [
+ gasCosts: [
{
- "type": "SEND",
- "price": "55972628",
- "estimate": "1586508",
- "limit": "2379762",
- "amount": "88801022103024",
- "amountUSD": "0.14",
- "token": {
- "address": "0x0000000000000000000000000000000000000000",
- "symbol": "ETH",
- "decimals": 18,
- "chainId": 10,
- "name": "ETH",
- "coinKey": "ETH",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
- "priceUSD": "1573.41"
- }
- }
+ type: "SEND",
+ price: "55972628",
+ estimate: "1586508",
+ limit: "2379762",
+ amount: "88801022103024",
+ amountUSD: "0.14",
+ token: {
+ address: "0x0000000000000000000000000000000000000000",
+ symbol: "ETH",
+ decimals: 18,
+ chainId: 10,
+ name: "ETH",
+ coinKey: "ETH",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
+ priceUSD: "1573.41",
+ },
+ },
],
- "executionDuration": 70,
- "fromAmountUSD": "0.00",
- "toAmountUSD": "0.00"
+ executionDuration: 70,
+ fromAmountUSD: "0.00",
+ toAmountUSD: "0.00",
},
- "includedSteps": [
+ includedSteps: [
{
- "id": "6ae78e41-66a0-4435-8e66-a5fcdb92c785",
- "type": "protocol",
- "action": {
- "fromChainId": 10,
- "fromAmount": "2",
- "fromToken": {
- "address": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "chainId": 10,
- "symbol": "aOptUSDC",
- "decimals": 6,
- "name": "Aave Optimism USDC",
- "priceUSD": "1",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
- "coinKey": "aOptUSDC"
+ id: "6ae78e41-66a0-4435-8e66-a5fcdb92c785",
+ type: "protocol",
+ action: {
+ fromChainId: 10,
+ fromAmount: "2",
+ fromToken: {
+ address: "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ chainId: 10,
+ symbol: "aOptUSDC",
+ decimals: 6,
+ name: "Aave Optimism USDC",
+ priceUSD: "1",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
+ coinKey: "aOptUSDC",
},
- "toChainId": 10,
- "toToken": {
- "address": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "chainId": 10,
- "symbol": "aOptUSDC",
- "decimals": 6,
- "name": "Aave Optimism USDC",
- "priceUSD": "1",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
- "coinKey": "aOptUSDC"
+ toChainId: 10,
+ toToken: {
+ address: "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ chainId: 10,
+ symbol: "aOptUSDC",
+ decimals: 6,
+ name: "Aave Optimism USDC",
+ priceUSD: "1",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
+ coinKey: "aOptUSDC",
},
- "slippage": 0.005,
- "fromAddress": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
- "toAddress": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE"
+ slippage: 0.005,
+ fromAddress: "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
+ toAddress: "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
},
- "estimate": {
- "fromAmount": "2",
- "toAmount": "2",
- "toAmountMin": "2",
- "tool": "feeCollection",
- "approvalAddress": "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9",
- "gasCosts": [
+ estimate: {
+ fromAmount: "2",
+ toAmount: "2",
+ toAmountMin: "2",
+ tool: "feeCollection",
+ approvalAddress: "0xbD6C7B0d2f68c2b7805d88388319cfB6EcB50eA9",
+ gasCosts: [
{
- "type": "SEND",
- "amount": "130000",
- "token": {
- "address": "0x0000000000000000000000000000000000000000",
- "symbol": "ETH",
- "decimals": 18,
- "chainId": 10,
- "name": "ETH",
- "coinKey": "ETH",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
- "priceUSD": "1573.41"
+ type: "SEND",
+ amount: "130000",
+ token: {
+ address: "0x0000000000000000000000000000000000000000",
+ symbol: "ETH",
+ decimals: 18,
+ chainId: 10,
+ name: "ETH",
+ coinKey: "ETH",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
+ priceUSD: "1573.41",
},
- "price": "55972628",
- "limit": "200000",
- "estimate": "200000",
- "amountUSD": "0.01"
- }
+ price: "55972628",
+ limit: "200000",
+ estimate: "200000",
+ amountUSD: "0.01",
+ },
],
- "feeCosts": [
+ feeCosts: [
{
- "name": "Integrator Fee",
- "description": "Fee shared between integrator and LI.FI",
- "token": {
- "address": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "chainId": 10,
- "symbol": "aOptUSDC",
- "decimals": 6,
- "name": "Aave Optimism USDC",
- "priceUSD": "1",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
- "coinKey": "aOptUSDC"
+ name: "Integrator Fee",
+ description: "Fee shared between integrator and LI.FI",
+ token: {
+ address: "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ chainId: 10,
+ symbol: "aOptUSDC",
+ decimals: 6,
+ name: "Aave Optimism USDC",
+ priceUSD: "1",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
+ coinKey: "aOptUSDC",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0",
- "included": true
- }
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0",
+ included: true,
+ },
],
- "executionDuration": 0
+ executionDuration: 0,
+ },
+ tool: "feeCollection",
+ toolDetails: {
+ key: "feeCollection",
+ name: "Integrator Fee",
+ logoURI:
+ "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/protocols/feeCollection.png",
},
- "tool": "feeCollection",
- "toolDetails": {
- "key": "feeCollection",
- "name": "Integrator Fee",
- "logoURI": "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/protocols/feeCollection.png"
- }
},
{
- "id": "10c780b1-475e-4cf7-b671-96ae464f3419",
- "type": "swap",
- "action": {
- "fromChainId": 10,
- "fromAmount": "2",
- "fromToken": {
- "address": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "chainId": 10,
- "symbol": "aOptUSDC",
- "decimals": 6,
- "name": "Aave Optimism USDC",
- "priceUSD": "1",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
- "coinKey": "aOptUSDC"
+ id: "10c780b1-475e-4cf7-b671-96ae464f3419",
+ type: "swap",
+ action: {
+ fromChainId: 10,
+ fromAmount: "2",
+ fromToken: {
+ address: "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ chainId: 10,
+ symbol: "aOptUSDC",
+ decimals: 6,
+ name: "Aave Optimism USDC",
+ priceUSD: "1",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/0x625e7708f30ca75bfd92586e17077590c60eb4cd/edbff4857cf186c17bfdac67f2b0e6b1.png",
+ coinKey: "aOptUSDC",
},
- "toChainId": 10,
- "toToken": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ toChainId: 10,
+ toToken: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "slippage": 0.005,
- "fromAddress": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
- "toAddress": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE"
+ slippage: 0.005,
+ fromAddress: "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
+ toAddress: "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
},
- "estimate": {
- "tool": "1inch",
- "fromAmount": "2",
- "toAmount": "2",
- "toAmountMin": "2",
- "approvalAddress": "0x1111111254eeb25477b68fb85ed929f73a960582",
- "executionDuration": 30,
- "feeCosts": [],
- "gasCosts": [
+ estimate: {
+ tool: "1inch",
+ fromAmount: "2",
+ toAmount: "2",
+ toAmountMin: "2",
+ approvalAddress: "0x1111111254eeb25477b68fb85ed929f73a960582",
+ executionDuration: 30,
+ feeCosts: [],
+ gasCosts: [
{
- "type": "SEND",
- "price": "55972628",
- "estimate": "518508",
- "limit": "777762",
- "amount": "29022255399024",
- "amountUSD": "0.05",
- "token": {
- "address": "0x0000000000000000000000000000000000000000",
- "symbol": "ETH",
- "decimals": 18,
- "chainId": 10,
- "name": "ETH",
- "coinKey": "ETH",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
- "priceUSD": "1573.41"
- }
- }
+ type: "SEND",
+ price: "55972628",
+ estimate: "518508",
+ limit: "777762",
+ amount: "29022255399024",
+ amountUSD: "0.05",
+ token: {
+ address: "0x0000000000000000000000000000000000000000",
+ symbol: "ETH",
+ decimals: 18,
+ chainId: 10,
+ name: "ETH",
+ coinKey: "ETH",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
+ priceUSD: "1573.41",
+ },
+ },
],
- "toolData": {
- "fromToken": {
- "address": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "symbol": "aOptUSDC",
- "name": "Aave Optimism USDC",
- "decimals": 6
+ toolData: {
+ fromToken: {
+ address: "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ symbol: "aOptUSDC",
+ name: "Aave Optimism USDC",
+ decimals: 6,
},
- "toToken": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC",
- "name": "USD Coin",
- "decimals": 6,
- "logoURI": "https://tokens.1inch.io/0x7f5c764cbc14f9669b88837ca1490cca17c31607.png",
- "tags": [
- "PEG:USD",
- "tokens"
- ]
+ toToken: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC",
+ name: "USD Coin",
+ decimals: 6,
+ logoURI:
+ "https://tokens.1inch.io/0x7f5c764cbc14f9669b88837ca1490cca17c31607.png",
+ tags: ["PEG:USD", "tokens"],
},
- "estimatedGas": 518508,
- "protocols": [
+ estimatedGas: 518508,
+ protocols: [
[
[
{
- "name": "OPTIMISM_AAVE_V3",
- "part": 100,
- "fromTokenAddress": "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
- "toTokenAddress": "0x7f5c764cbc14f9669b88837ca1490cca17c31607"
- }
- ]
- ]
+ name: "OPTIMISM_AAVE_V3",
+ part: 100,
+ fromTokenAddress:
+ "0x625e7708f30ca75bfd92586e17077590c60eb4cd",
+ toTokenAddress: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ },
+ ],
+ ],
],
- "toTokenAmount": "2",
- "fromTokenAmount": "2"
- }
+ toTokenAmount: "2",
+ fromTokenAmount: "2",
+ },
+ },
+ tool: "1inch",
+ toolDetails: {
+ key: "1inch",
+ name: "1inch",
+ logoURI:
+ "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/exchanges/oneinch.png",
},
- "tool": "1inch",
- "toolDetails": {
- "key": "1inch",
- "name": "1inch",
- "logoURI": "https://raw.githubusercontent.com/lifinance/types/main/src/assets/icons/exchanges/oneinch.png"
- }
},
{
- "id": "b1342367-306c-49e6-b568-c0b8d363b20d",
- "type": "cross",
- "action": {
- "fromChainId": 10,
- "fromAmount": "2",
- "fromToken": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ id: "b1342367-306c-49e6-b568-c0b8d363b20d",
+ type: "cross",
+ action: {
+ fromChainId: 10,
+ fromAmount: "2",
+ fromToken: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "toChainId": 1,
- "toToken": {
- "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
- "symbol": "USDC",
- "decimals": 6,
- "chainId": 1,
- "name": "USD Coin",
- "coinKey": "USDC",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ toChainId: 1,
+ toToken: {
+ address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
+ symbol: "USDC",
+ decimals: 6,
+ chainId: 1,
+ name: "USD Coin",
+ coinKey: "USDC",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "slippage": 0.005,
- "destinationGasConsumption": "0",
- "destinationCallData": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
- "fromAddress": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
- "toAddress": "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7"
+ slippage: 0.005,
+ destinationGasConsumption: "0",
+ destinationCallData:
+ "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ fromAddress: "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
+ toAddress: "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7",
},
- "estimate": {
- "tool": "stargate",
- "fromAmount": "2",
- "toAmount": "2",
- "toAmountMin": "2",
- "approvalAddress": "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b",
- "executionDuration": 40,
- "feeCosts": [
+ estimate: {
+ tool: "stargate",
+ fromAmount: "2",
+ toAmount: "2",
+ toAmountMin: "2",
+ approvalAddress: "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b",
+ executionDuration: 40,
+ feeCosts: [
{
- "name": "LayerZero fees",
- "description": "Infrastructure fee paid in native token",
- "token": {
- "address": "0x0000000000000000000000000000000000000000",
- "symbol": "ETH",
- "decimals": 18,
- "chainId": 10,
- "name": "ETH",
- "coinKey": "ETH",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
- "priceUSD": "1573.41"
+ name: "LayerZero fees",
+ description: "Infrastructure fee paid in native token",
+ token: {
+ address: "0x0000000000000000000000000000000000000000",
+ symbol: "ETH",
+ decimals: 18,
+ chainId: 10,
+ name: "ETH",
+ coinKey: "ETH",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
+ priceUSD: "1573.41",
},
- "amount": "3358382275654522",
- "amountUSD": "5.28",
- "percentage": "2642056.1282",
- "included": false
+ amount: "3358382275654522",
+ amountUSD: "5.28",
+ percentage: "2642056.1282",
+ included: false,
},
{
- "name": "Equilibrium fees",
- "description": "The fee paid to users who rebalance tokens for the stargate protocol",
- "token": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ name: "Equilibrium fees",
+ description:
+ "The fee paid to users who rebalance tokens for the stargate protocol",
+ token: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0.0000",
- "included": true
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0.0000",
+ included: true,
},
{
- "name": "Liquidity provider fees",
- "description": "The fee paid to liquidity providers for the stargate protocol",
- "token": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ name: "Liquidity provider fees",
+ description:
+ "The fee paid to liquidity providers for the stargate protocol",
+ token: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0.0000",
- "included": true
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0.0000",
+ included: true,
},
{
- "name": "Protocol fees",
- "description": "The fee paid to the stargate protocol",
- "token": {
- "address": "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
- "symbol": "USDC.e",
- "decimals": 6,
- "chainId": 10,
- "name": "Bridged USD Coin",
- "coinKey": "USDCe",
- "logoURI": "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
- "priceUSD": "1"
+ name: "Protocol fees",
+ description: "The fee paid to the stargate protocol",
+ token: {
+ address: "0x7f5c764cbc14f9669b88837ca1490cca17c31607",
+ symbol: "USDC.e",
+ decimals: 6,
+ chainId: 10,
+ name: "Bridged USD Coin",
+ coinKey: "USDCe",
+ logoURI:
+ "https://static.debank.com/image/coin/logo_url/usdc/e87790bfe0b3f2ea855dc29069b38818.png",
+ priceUSD: "1",
},
- "amount": "0",
- "amountUSD": "0.00",
- "percentage": "0.0000",
- "included": true
- }
+ amount: "0",
+ amountUSD: "0.00",
+ percentage: "0.0000",
+ included: true,
+ },
],
- "gasCosts": [
+ gasCosts: [
{
- "type": "SEND",
- "price": "55972628",
- "estimate": "525000",
- "limit": "787500",
- "amount": "29385629700000",
- "amountUSD": "0.05",
- "token": {
- "address": "0x0000000000000000000000000000000000000000",
- "symbol": "ETH",
- "decimals": 18,
- "chainId": 10,
- "name": "ETH",
- "coinKey": "ETH",
- "logoURI": "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
- "priceUSD": "1573.41"
- }
- }
+ type: "SEND",
+ price: "55972628",
+ estimate: "525000",
+ limit: "787500",
+ amount: "29385629700000",
+ amountUSD: "0.05",
+ token: {
+ address: "0x0000000000000000000000000000000000000000",
+ symbol: "ETH",
+ decimals: 18,
+ chainId: 10,
+ name: "ETH",
+ coinKey: "ETH",
+ logoURI:
+ "https://static.debank.com/image/op_token/logo_url/op/d61441782d4a08a7479d54aea211679e.png",
+ priceUSD: "1573.41",
+ },
+ },
],
- "toolData": {
- "sourcePoolId": "1",
- "destinationPoolId": "1",
- "router": "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b",
- "layerZeroChainId": 101
- }
+ toolData: {
+ sourcePoolId: "1",
+ destinationPoolId: "1",
+ router: "0xB0D502E938ed5f4df2E681fE6E419ff29631d62b",
+ layerZeroChainId: 101,
+ },
},
- "tool": "stargate",
- "toolDetails": {
- "key": "stargate",
- "name": "Stargate",
- "logoURI": "https://raw.githubusercontent.com/lifinance/types/5685c638772f533edad80fcb210b4bb89e30a50f/src/assets/icons/bridges/stargate.png"
- }
- }
+ tool: "stargate",
+ toolDetails: {
+ key: "stargate",
+ name: "Stargate",
+ logoURI:
+ "https://raw.githubusercontent.com/lifinance/types/5685c638772f533edad80fcb210b4bb89e30a50f/src/assets/icons/bridges/stargate.png",
+ },
+ },
],
- "integrator": "hexa-lite",
- "transactionRequest": {
- "data": "0xed17861900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000076098db3c9bcf693438ae393a90b6b853c20c5e32f5d71f51e2415bcdc5ee66ec0d0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c31607000000000000000000000000d2b2a35039270d8fdca84e7c15e1461dad9f3ad70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000873746172676174650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009686578612d6c69746500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000bd6c7b0d2f68c2b7805d88388319cfb6ecb50ea9000000000000000000000000bd6c7b0d2f68c2b7805d88388319cfb6ecb50ea9000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ec627be9a3c1f30ef5fbf39609d2d6ab947e37e000000000000000000000000000000000000000000000000000000000000000000000000000000001111111254eeb25477b68fb85ed929f73a9605820000000000000000000000001111111254eeb25477b68fb85ed929f73a960582000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c31607000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022412aa3caf000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c31607000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000007c4120794a61358d6845594f94dc1db02a252b5b4814ad002469328dec0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c3160700000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111254eeb25477b68fb85ed929f73a960582000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bee6e4e8f737a000000000000000000000000d2b2a35039270d8fdca84e7c15e1461dad9f3ad7000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000014d2b2a35039270d8fdca84e7c15e1461dad9f3ad70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
- "to": "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
- "value": "0x0bee6e4e8f737a",
- "from": "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7",
- "chainId": 10,
- "gasPrice": "0x03561314",
- "gasLimit": "0x244ff2"
- }
+ integrator: "hexa-lite",
+ transactionRequest: {
+ data: "0xed17861900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000076098db3c9bcf693438ae393a90b6b853c20c5e32f5d71f51e2415bcdc5ee66ec0d0000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c31607000000000000000000000000d2b2a35039270d8fdca84e7c15e1461dad9f3ad70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000873746172676174650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009686578612d6c69746500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000bd6c7b0d2f68c2b7805d88388319cfb6ecb50ea9000000000000000000000000bd6c7b0d2f68c2b7805d88388319cfb6ecb50ea9000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084eedd56e1000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ec627be9a3c1f30ef5fbf39609d2d6ab947e37e000000000000000000000000000000000000000000000000000000000000000000000000000000001111111254eeb25477b68fb85ed929f73a9605820000000000000000000000001111111254eeb25477b68fb85ed929f73a960582000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c31607000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022412aa3caf000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba000000000000000000000000625e7708f30ca75bfd92586e17077590c60eb4cd0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c31607000000000000000000000000b63aae6c353636d66df13b89ba4425cfe13d10ba0000000000000000000000001231deb6f5749ef6ce6943a275a1d3e7486f4eae000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009a00000000000000000000000000000000000000000000000000000000007c4120794a61358d6845594f94dc1db02a252b5b4814ad002469328dec0000000000000000000000007f5c764cbc14f9669b88837ca1490cca17c3160700000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111254eeb25477b68fb85ed929f73a960582000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bee6e4e8f737a000000000000000000000000d2b2a35039270d8fdca84e7c15e1461dad9f3ad7000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000014d2b2a35039270d8fdca84e7c15e1461dad9f3ad70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ to: "0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE",
+ value: "0x0bee6e4e8f737a",
+ from: "0xd2b2A35039270d8fDcA84E7c15E1461daD9F3Ad7",
+ chainId: 10,
+ gasPrice: "0x03561314",
+ gasLimit: "0x244ff2",
+ },
} as LiFiQuoteResponse;
/**
* Sending the Transaction
*/
export const sendTransaction = async (
- quote: LiFiQuoteResponse,
- provider: ethers.providers.Web3Provider,
+ quote: LiFiQuoteResponse,
+ provider: ethers.providers.Web3Provider
) => {
const signer = provider.getSigner();
const tx = await signer.sendTransaction(quote.transactionRequest);
const receipt = await tx.wait();
return receipt;
-}
+};
/**
* Checking allowence
@@ -746,69 +778,139 @@ export const sendTransaction = async (
*/
export const checkAndSetAllowance = async (
- provider: ethers.providers.Web3Provider,
- tokenAddress: string,
- approvalAddress: string,
- amount: string,
+ provider: ethers.providers.Web3Provider,
+ tokenAddress: string,
+ approvalAddress: string,
+ amount: string
) => {
- // Transactions with the native token don't need approval
- if (tokenAddress === ethers.constants.AddressZero) {
- return
- }
- const signer = provider.getSigner();
- const erc20 = new Contract(tokenAddress, ERC20_ABI, signer);
- const address = await signer.getAddress();
- const allowance = await erc20.allowance(address, approvalAddress);
- if (allowance.lt(amount)) {
- const approveTx = await erc20.approve(approvalAddress, amount);
- await approveTx.wait();
- }
-}
+ // Transactions with the native token don't need approval
+ if (tokenAddress === ethers.constants.AddressZero) {
+ return;
+ }
+ const signer = provider.getSigner();
+ const erc20 = new Contract(tokenAddress, ERC20_ABI, signer);
+ const address = await signer.getAddress();
+ const allowance = await erc20.allowance(address, approvalAddress);
+ if (allowance.lt(amount)) {
+ const approveTx = await erc20.approve(approvalAddress, amount);
+ await approveTx.wait();
+ }
+};
/**
* Perform the swap with LiFi
- * @param ops
- * @param provider
+ * @param ops
+ * @param provider
*/
export const swapWithLiFi = async (
ops: {
- fromChain: string,
- toChain: string,
- fromToken: string,
- toToken: string,
- fromAmount: string,
- fromAddress: string,
+ fromChain: string;
+ toChain: string;
+ fromToken: string;
+ toToken: string;
+ fromAmount: string;
+ fromAddress: string;
},
- provider: ethers.providers.Web3Provider,
+ provider: ethers.providers.Web3Provider
) => {
const quote = await getQuote(
- ops.fromChain,
- ops.toChain,
- ops.fromToken,
- ops.toToken,
- ops.fromAmount,
- ops.fromAddress,
+ ops.fromChain,
+ ops.toChain,
+ ops.fromToken,
+ ops.toToken,
+ ops.fromAmount,
+ ops.fromAddress
);
await checkAndSetAllowance(
- provider,
- quote.action.fromToken.address,
- quote.estimate.approvalAddress,
- quote.action.fromAmount,
+ provider,
+ quote.action.fromToken.address,
+ quote.estimate.approvalAddress,
+ quote.action.fromAmount
);
const receipt = await sendTransaction(quote, provider);
return receipt;
-}
+};
+
+export const getTokensPrice = async (tokens: IAsset[]) => {
+ if (!tokens.length) {
+ return [];
+ }
+ const options = { method: "GET", headers: { accept: "application/json" } };
+ const chainIds = tokens
+ .map((token) => token.chain?.id)
+ .filter(Boolean)
+ .join(",");
+ const url = `https://li.quest/v1/tokens?chains=${chainIds}`;
+ let tokensResponse!: { [key: string]: Token[] };
+ //use Cache API to store the response
+ try {
+ const cache = await caches.open("hexa-lite_li-quest");
+ cache.keys;
+ const { data, timestamp } =
+ ((await cache.match(url)?.then((r) => r?.json())) as {
+ data: {
+ tokens: {
+ [key: string]: Token[];
+ };
+ };
+ timestamp: number;
+ }) || {};
+ if (data && Date.now() - timestamp < 1000 * 60 * 15) {
+ tokensResponse = data.tokens;
+ console.log("[INFO] Tokens with price from cache:", data);
+ } else {
+ const response = await fetch(url, options);
+ const responseData = (await response.json()) as {
+ tokens: { [key: string]: Token[] };
+ };
+ cache.put(
+ url,
+ new Response(
+ JSON.stringify({
+ data: responseData,
+ timestamp: Date.now(),
+ })
+ )
+ );
+ tokensResponse = responseData.tokens;
+ }
+ } catch (error) {
+ throw error;
+ }
+ const tokenWithPrice: IAsset[] = [];
+ for (const token of tokens) {
+ const index = tokensResponse[token.chain?.id as number].findIndex(
+ (t) => t.symbol === token.symbol
+ );
+ if (index > -1) {
+ tokenWithPrice.push({
+ ...token,
+ priceUsd: Number(
+ tokensResponse[token.chain?.id as number][index].priceUSD
+ ),
+ balanceUsd:
+ token.balance *
+ Number(tokensResponse[token.chain?.id as number][index].priceUSD),
+ thumbnail:
+ token.thumbnail ||
+ tokensResponse[token.chain?.id as number][index].logoURI,
+ });
+ }
+ }
+ // console.log("[INFO] Tokens with price:", tokenWithPrice);
+ return tokenWithPrice;
+};
export const LIFI_CONFIG = Object.freeze({
// integrator: "cra-example",
integrator: process.env.NEXT_PUBLIC_APP_IS_PROD ? "hexa-lite" : "",
- fee: 0.005,
+ fee: 0.01,
variant: "expandable",
insurance: true,
containerStyle: {
border: `1px solid rgba(var(--ion-color-primary-rgb), 0.4);`,
borderRadius: "32px",
- filter: "drop-shadow(rgba(var(--ion-color-primary-rgb), .1) 0px 0px 50px )"
+ filter: "drop-shadow(rgba(var(--ion-color-primary-rgb), .1) 0px 0px 50px )",
},
theme: {
shape: {
@@ -817,20 +919,24 @@ export const LIFI_CONFIG = Object.freeze({
},
palette: {
grey: {
- "800": 'rgba(var(--ion-color-primary-rgb), 0.2)'
+ "800": "rgba(var(--ion-color-primary-rgb), 0.4)",
+ },
+ text: {
+ primary: "rgb(var(--ion-text-color-rgb))",
+ secondary: "rgba(var(--ion-text-color-rgb), 0.6)",
},
background: {
- paper: "#0f1629", // green
+ paper: "rgb(var(--item-background-shader-rgb))", // green
// default: '#182449',
},
primary: {
main: "#0090FF",
- contrastText: "#fff",
+ contrastText: "rgb(var(--ion-text-color.rgb))",
},
secondary: {
- main: '#4CCCE6',
- contrastText: "#fff",
- }
+ main: "#4CCCE6",
+ contrastText: "rgb(var(--ion-text-color.rgb))",
+ },
},
},
languages: {
@@ -838,4 +944,4 @@ export const LIFI_CONFIG = Object.freeze({
},
appearance: "dark",
hiddenUI: [HiddenUI.Appearance, HiddenUI.PoweredBy, HiddenUI.Language],
-});
\ No newline at end of file
+});
diff --git a/src/servcies/medium.service.ts b/src/servcies/medium.service.ts
new file mode 100644
index 00000000..ded85ef7
--- /dev/null
+++ b/src/servcies/medium.service.ts
@@ -0,0 +1,57 @@
+
+export const getPublications = async (): Promise<{
+ url: string;
+ title: string;
+ imgUrl: string;
+ dateTime: number;
+ short: string;
+}[]> => {
+ const publications = [
+ {
+ url: 'https://medium.com/@hexaonelabs/hexa-lite-transforming-the-landscape-of-decentralized-finance-c5ba5079c021',
+ title: 'Hexa Lite: Transforming the Landscape of Decentralized Finance',
+ imgUrl: 'https://miro.medium.com/v2/resize:fit:350/format:webp/1*o_hhgfFIZNQcGeQrFZm2rA.png',
+ dateTime: Date.parse('01/17/2024'),
+ short: 'Welcome to the future of decentralized finance with Hexa Lite, a groundbreaking platform that opens the doors of DeFi to a broader audience. Designed to simplify access to decentralized financial services, Hexa Lite pushes the boundaries to provide a simple and accessible user experience for all.'
+ },
+ {
+ url: 'https://medium.com/@hexaonelabs/demystifying-defi-how-hexa-lite-makes-decentralized-finance-accessible-to-all-dd62dbe06410',
+ title: 'Demystifying DeFi: How Hexa Lite Makes Decentralized Finance Accessible to All',
+ imgUrl: 'https://miro.medium.com/v2/resize:fit:350/format:webp/1*0fw1ACIj4zvS-FYhwp8TpA.png',
+ dateTime: Date.parse('01/24/2024'),
+ short: 'Decentralized Finance, or DeFi, is undoubtedly one of the hottest topics in the current financial landscape. However, behind the technical jargon and apparent opportunities, lies a complex and often intimidating world, deterring many potential users.'
+ },
+ {
+ url: 'https://medium.com/@hexaonelabs/the-benefits-of-liquid-staking-with-hexa-lite-maximize-your-earnings-without-compromise-2e3852ead822',
+ title: 'The Benefits of Liquid Staking with Hexa Lite: Maximize Your Earnings Without Compromise',
+ imgUrl: 'https://miro.medium.com/v2/resize:fit:350/format:webp/1*ClE8BjpiHl2NYF4Gu69JNg.png',
+ dateTime: Date.parse('01/31/2024'),
+ short: 'Decentralized Finance (DeFi) is a revolution, but for many, liquid staking may seem like a mountain to climb. In this article, we will explore the complexities of liquid staking in DeFi and discover how Hexa Lite simplifies this experience, giving users the opportunity to maximize their earnings without compromise.'
+ },
+ {
+ url: 'https://medium.com/@hexaonelabs/hexa-lite-your-gateway-to-seamless-authentication-c7f7feff5281',
+ title: 'Hexa Lite: Your Gateway to Seamless Authentication',
+ imgUrl: 'https://miro.medium.com/v2/resize:fit:350/format:webp/1*P89ZVTGsO0VWWw6jtZdEwA.png',
+ dateTime: Date.parse('03/25/2024'),
+ short: 'Experience the next level of simplicity and security with Hexa Lite’s one-click login process. Say goodbye to cumbersome account creation steps and hello to effortless access to our platform. With just a few clicks, you can sign up or log in using your email address or preferred social authentication method.'
+ },
+ {
+ url: 'https://medium.com/@hexaonelabs/introducing-our-new-mobile-and-desktop-application-interface-try-it-now-7e0df3c1fdcd',
+ title: 'Introducing Our New Mobile and Desktop Application Interface: Try It Now!',
+ imgUrl: 'https://miro.medium.com/v2/resize:fit:350/format:webp/1*hTGoxycGxTQO6Iw94Y4iUw.png',
+ dateTime: Date.parse('03/26/2024'),
+ short: 'We are excited to announce the launch of our new mobile and desktop application interface! Our team has been working tirelessly to create a seamless and intuitive user experience that will make your journey with Hexa Lite even more enjoyable.'
+ },
+ {
+ url: 'https://medium.com/@hexaonelabs/hexa-lite-tech-roadmap-transforming-dreams-into-reality-90a3d75b13f1',
+ title: 'Hexa Lite Tech Roadmap: Transforming Dreams into Reality',
+ imgUrl: 'https://miro.medium.com/v2/resize:fit:350/format:webp/1*hTGoxycGxTQO6Iw94Y4iUw.png',
+ dateTime: Date.parse('04/09/2024'),
+ short: 'At Hexa Lite, we blending cutting-edge technology with a passion for progress to create a financial ecosystem that’s as dynamic as it is accessible. One of the pillars of our success? Our commitment to open-source development.'
+ }
+ ];
+
+ return publications
+ .sort((a, b) => b.dateTime - a.dateTime)
+ .slice(0, 4);
+}
\ No newline at end of file
diff --git a/src/servcies/qrcode.service.ts b/src/servcies/qrcode.service.ts
new file mode 100644
index 00000000..0dd931f3
--- /dev/null
+++ b/src/servcies/qrcode.service.ts
@@ -0,0 +1,12 @@
+
+
+export const getQrcodeAsSVG = async (value: string) => {
+ const QRCode = await import("qrcode").then((module) => module);
+ try {
+ return await QRCode.toString(value, {
+ type: 'svg'
+ })
+ } catch (err: any) {
+ throw new Error(err?.message || "Error generating QR code")
+ }
+}
\ No newline at end of file
diff --git a/src/store/actions.ts b/src/store/actions.ts
index 6c1e239a..1fb64e9c 100755
--- a/src/store/actions.ts
+++ b/src/store/actions.ts
@@ -1,5 +1,5 @@
import { MarketPool } from '@/pool/Market.pool';
-import Store, { IPoolsState, IWeb3State } from '.';
+import Store, { IAppSettings, IPoolsState, IWeb3State } from '.';
export const setWeb3State = (web3State: IWeb3State) => {
Store.update(s => {
@@ -31,4 +31,17 @@ export const patchMarketPoolsState = (marketsPools: MarketPool[]) => {
});
};
+export const setErrorState = (error?: Error) => {
+ Store.update(s => {
+ s.error = error;
+ });
+};
+export const patchAppSettings = (appSettings: Partial) => {
+ Store.update(s => {
+ s.appSettings = {
+ ...s.appSettings,
+ ...appSettings
+ };
+ });
+}
\ No newline at end of file
diff --git a/src/store/effects/app-settings.effect.ts b/src/store/effects/app-settings.effect.ts
new file mode 100644
index 00000000..47489830
--- /dev/null
+++ b/src/store/effects/app-settings.effect.ts
@@ -0,0 +1,13 @@
+import { patchAppSettings } from "../actions";
+
+export const initializeAppSettings = async () => {
+ const settings = localStorage.getItem('hexa-lite_app_settings');
+ if (settings) {
+ try {
+ const parsedSettings = JSON.parse(settings);
+ patchAppSettings(parsedSettings);
+ } catch (error) {
+ throw new Error('Failed to parse app settings');
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/store/effects/pools.effect.ts b/src/store/effects/pools.effect.ts
index bad08fe0..d79cf46a 100644
--- a/src/store/effects/pools.effect.ts
+++ b/src/store/effects/pools.effect.ts
@@ -2,7 +2,7 @@ import { IUserSummary } from "@/interfaces/reserve.interface";
import { AavePool } from "@/pool/Aave.pool";
import { IMarketConfig, SolendPool } from "@/pool/solend.pool";
import { patchMarketPoolsState, patchPoolsState, setPoolsState } from "../actions";
-import { CHAIN_AVAILABLES, NETWORK } from "@/constants/chains";
+import { CHAIN_AVAILABLES as ALL_CHAINS, NETWORK } from "@/constants/chains";
import {
MARKETTYPE,
getMarkets,
@@ -14,6 +14,9 @@ import { getAssetIconUrl } from "@/utils/getAssetIconUrl";
import { MarketPool } from "@/pool/Market.pool";
import { PublicKey, Connection as SolanaClient } from "@solana/web3.js";
+// temporary disable Avalanche from the lending available networks
+const CHAIN_AVAILABLES = ALL_CHAINS.filter((chain) => chain.id !== NETWORK.avalanche);
+
const loadAavePools = async () => {
console.log("[INFO] {{loadAavePools}} init context... ");
const currentTimestamp = dayjs().unix();
@@ -123,17 +126,17 @@ const loadAaveUserSummary = async (walletAddress: string) => {
market,
currentTimestamp,
user: walletAddress,
+ }).catch(err => {
+ console.error("[ERROR] {{loadAaveUserSummary}} getUserSummaryAndIncentives: ", {err, market});
+ return [];
})
)
)
- .then((r) => r as IUserSummary[])
- .catch((error) => {
- console.error(
- "[ERROR] {{loadAaveUserSummary}} fetchUserSummaryAndIncentives: ",
- error
- );
- return null;
- });
+ .then((r) => r as IUserSummary[])
+ .catch(err => {
+ console.error("[ERROR] {{loadAaveUserSummary}} Promise.all() getUserSummaryAndIncentives: ", {err});
+ return [];
+ });
console.log(`[INFO] {{loadAaveUserSummary}} done: `, {
userSummaryAndIncentivesGroup,
});
diff --git a/src/store/effects/web3.effects.ts b/src/store/effects/web3.effects.ts
index 48e543ed..209080e7 100644
--- a/src/store/effects/web3.effects.ts
+++ b/src/store/effects/web3.effects.ts
@@ -1,6 +1,6 @@
import { CHAIN_DEFAULT } from "@/constants/chains";
import { MagicWalletUtils } from "@/network/MagicWallet";
-import { setWeb3State } from "../actions";
+import { setErrorState, setWeb3State } from "../actions";
export const initializeWeb3 = async (chainId: number = CHAIN_DEFAULT.id) => {
console.log(`[INFO] {{Web3Effect}} initializeWeb3() - `, chainId);
@@ -14,6 +14,7 @@ export const initializeWeb3 = async (chainId: number = CHAIN_DEFAULT.id) => {
if (magicUtils?.walletAddress) {
console.log('[INFO] {{Web3Effect}} load balance...');
await magicUtils.loadBalances().catch((err) => {
+ setErrorState(new Error(`Load wallet balances failed. Try again later.`));
console.error('[ERROR] {{Web3Effect}} load balance error: ', err?.message ? err.message : err);
});
}
@@ -35,8 +36,8 @@ export const initializeWeb3 = async (chainId: number = CHAIN_DEFAULT.id) => {
switchNetwork: async (chainId: number) => {
await initializeWeb3(chainId);
},
- loadAssets: async () => {
- await magicUtils.loadBalances().catch((err) => {
+ loadAssets: async (force?: boolean) => {
+ await magicUtils.loadBalances(force).catch((err) => {
console.error('[ERROR] {{Web3Effect}} load balance error: ', err?.message ? err.message : err);
});
setWeb3State({
@@ -44,6 +45,18 @@ export const initializeWeb3 = async (chainId: number = CHAIN_DEFAULT.id) => {
assets: magicUtils.assets,
});
},
+ transfer: async (ops: {
+ inputFromAmount: number;
+ inputToAddress: string;
+ inputFromAsset: string;
+ }) => {
+ const result = await magicUtils.sendToken(
+ ops.inputToAddress,
+ ops.inputFromAmount,
+ ops.inputFromAsset
+ );
+ console.log('[INFO] {{Web3Effect}} transfer result: ', result);
+ }
};
console.log('[INFO] {{Web3Effect}} state: ', state);
setWeb3State(state);
diff --git a/src/store/index.ts b/src/store/index.ts
index 5250c7b1..41eb5011 100755
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -14,7 +14,12 @@ export interface IWeb3State {
connectWallet(ops?: {email: string;}): Promise;
disconnectWallet(): Promise;
switchNetwork: (chainId: number) => Promise;
- loadAssets: () => Promise;
+ loadAssets: (force?: boolean) => Promise;
+ transfer: (ops: {
+ inputFromAmount: number;
+ inputToAddress: string;
+ inputFromAsset: string;
+ }) => Promise;
}
export interface IPoolsState {
@@ -24,9 +29,17 @@ export interface IPoolsState {
refresh: (type?: "init" | "userSummary") => Promise;
};
+export type IAppSettings = {
+ ui: {
+ hideCurrencieAmount: boolean;
+ };
+}
+
export interface IStore {
pools: IPoolsState,
- web3:IWeb3State
+ web3:IWeb3State,
+ error?: Error,
+ appSettings: IAppSettings;
}
const defaultState: IStore = Object.freeze({
@@ -56,6 +69,18 @@ const defaultState: IStore = Object.freeze({
loadAssets: async () => {
throw new Error("loadAssets function not implemented");
},
+ transfer: async (ops: {
+ inputFromAmount: number;
+ inputToAddress: string;
+ inputFromAsset: string;
+ }) => {
+ throw new Error("transfer function not implemented");
+ },
+ },
+ appSettings: {
+ ui: {
+ hideCurrencieAmount: false,
+ },
}
});
diff --git a/src/store/selectors.ts b/src/store/selectors.ts
index 2b18fc79..bead6c77 100755
--- a/src/store/selectors.ts
+++ b/src/store/selectors.ts
@@ -185,3 +185,8 @@ export const getProtocolSummaryState = createSelector(
return summary;
}
);
+
+
+export const getErrorState = createSelector(getState, (state) => state.error);
+
+export const getAppSettings = createSelector(getState, (state) => state.appSettings);
\ No newline at end of file
diff --git a/src/styles/global.scss b/src/styles/global.scss
index f54a9051..ce751f5b 100755
--- a/src/styles/global.scss
+++ b/src/styles/global.scss
@@ -5,8 +5,53 @@ $font-family: var(--font-outfit);//'Outfit', sans-serif;
html, body, ion-content {
font-size: 20px;
line-height: normal;
- background: '#182449';
+ background: #182449;
background: var(--background-gradient);
+}
+
+body {
+ .AppLogo {
+ background: url('/assets/images/logo-colored.svg');
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ min-width: 42px;
+ min-height: 42px;
+ cursor: pointer;
+ }
+
+ .shadowedElement {
+ box-shadow: 0 0px 50px -30px var(--ion-color-tertiary);
+ }
+ .dropShadowedElement {
+ filter: none;
+ }
+
+ &.dark {
+
+ .AppLogo {
+ background: url('/assets/images/logo.svg');
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+ min-width: 42px;
+ min-height: 42px;
+ }
+ img {
+ filter: brightness(.9) contrast(1.2);
+ }
+
+ .shadowedElement {
+ box-shadow: 0 0px 100px -20px var(--ion-color-tertiary);
+ }
+ .dropShadowedElement {
+ filter: drop-shadow(0px 0px 60px rgba(var(--ion-color-tertiary-rgb),0.5));
+ }
+ }
+}
+
+
+ion-content {
--background: var(--background-gradient);
}
@@ -33,6 +78,18 @@ p {
color-scheme: light dark;
}
+// Hide scrollbar for Chrome, Safari and Opera
+ion-content::part(scroll)::-webkit-scrollbar {
+ display: none;
+}
+
+// Hide scrollbar for IE, Edge and Firefox
+ion-content::part(scroll) {
+ -ms-overflow-style: none; // IE and Edge
+ scrollbar-width: none; // Firefox
+}
+
+
.header-background {
-webkit-backdrop-filter: blur(20px)!important;
backdrop-filter: blur(20px)!important;
@@ -133,6 +190,10 @@ ion-popover.profil-popover {
}
}
+ion-modal {
+ --border-radius: 32px;
+}
+
ion-popover.points-popover {
--width: 100%;
--max-width: 350px;
@@ -194,10 +255,19 @@ ion-alert.modalAlert {
--border-radius: 0;
}
+.modal-sheet {
+ --width: 100%;
+ --border-radius: 32px;
+}
+
+ion-accordion.accordion-expanding > [slot=header] .ion-accordion-toggle-icon,
+ion-accordion.accordion-expanded > [slot=header] .ion-accordion-toggle-icon {
+ transform: rotate(180deg) scale(0.6);
+}
.ion-accordion-toggle-icon {
color: var(--ion-color-primary);
- transform: scale(0.8);
+ transform: scale(0.6);
}
ion-accordion {
@@ -276,21 +346,97 @@ ion-card {
-webkit-filter: drop-shadow(rgba(var(--ion-color-primary-rgb), .2) 0px 0px 50px);
filter: drop-shadow(rgba(var(--ion-color-primary-rgb), .2) 0px 0px 50px );
- box-shadow: 0 0 12px 0px rgb(0 0 0 / 25%);
+}
+body.dark {
+ .widgetWrapper, ion-card:not(.ion-color), .leaderboardPage ion-list {
+ box-shadow: 0 0 12px 0px rgb(0 0 0 / 25%);
+ }
}
.welcomeSection {
+
+ .logoTextContainer {
+ margin-top: 6rem;
+ text-align: left;
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+
+ .homeLogo {
+ min-width: 116px;
+ min-height: 116px;
+ margin-right: 1rem;
+ }
+ }
+
+ .homeActionBtns {
+ display: flex;
+ align-items: center;
+ flex-direction: row;
+ }
+
+ > ion-row > ion-col {
+ ion-button {
+ margin-top: 2rem ;
+ }
+ }
+
+ @media screen and (max-width: 991px) {
+
+ > ion-row > ion-col {
+ ion-button {
+ display: table;
+ margin: 2rem auto;
+ }
+ }
+
+ .logoTextContainer {
+ margin-top: 3rem;
+ flex-direction: column;
+ text-align: center;
+
+ .homeLogo {
+ min-width: 148px;
+ min-height: 148px;
+ margin-top: 6rem;
+ margin-bottom: 1rem;
+ margin-right: 0rem;
+ }
+ }
+
+ ion-image.homeLogo {
+ margin: auto
+ }
+
+
+ .homeActionBtns {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+
+ ion-button {
+ margin: 1rem auto;
+ &:first-of-type {
+ margin: 2rem auto 0;
+ }
+ }
+ }
+
+ }
+
h1.homeTitle {
display: block;
font-weight: bold;
- margin-top: 2rem;
+ margin-top: 0rem;
letter-spacing: -0.1rem;
font-size: 4.8rem;
line-height: 4.2rem;
+ text-align: left;
- @media screen and (max-width: 428px) {
+ @media screen and (max-width: 991px) {
font-size: 3.8rem;
line-height: 3.4rem;
+ text-align: center;
}
}
@@ -313,12 +459,12 @@ ion-card {
line-height: 1.4rem;
&.slogan {
- font-size: 2rem;
+ font-size: 1.4rem;
margin: 0rem;
line-height: 1.8rem;
- @media screen and (max-width: 428px) {
- font-size: 1.6rem;
+ @media screen and (max-width: 991px) {
+ font-size: 1.4rem;
line-height: 1.6rem;
}
}
@@ -326,9 +472,12 @@ ion-card {
}
.rowSection {
- min-height: 100vh;
+ min-height: 90vh;
}
+ .shaderContener {
+ background: var(--background-shader);
+ }
.withPadding {
padding-top: 5rem;
padding-bottom: 5rem;
@@ -543,7 +692,7 @@ ion-accordion:not(.faq) {
}
ion-item {
- --background: rgba(0,0,0, 0.5) !important;
+ --background: rgba(var(--item-background-shader-rgb), 0.5) !important;
--padding-top: 0.4rem;
--padding-bottom: 0.4rem;
--padding-start: 0;
@@ -560,15 +709,15 @@ ion-accordion:not(.faq) {
}
&:hover {
- --background: rgba(var(--ion-color-light-rgb), 0.1) !important;
+ --background: rgba(var(--ion-color-primary-rgb), 0.1) !important;
ion-fab-button {
opacity: 1;
}
}
&.item-disabled {
- opacity: 1;
- --background: rgba(0,0,0, 0.5) !important;
+ opacity: 0.8;
+ //--background: rgba(var(--ion-color-dark-contrast-rgb), 0.1) !important;
pointer-events: none;
ion-grid {
@@ -621,6 +770,36 @@ ion-accordion:not(.faq) {
}
}
+ion-accordion.networkList {
+ &:not(.accordion-collapsed):not(.accordion-collapsing) {
+ background: transparent!important;
+ }
+ &:hover,
+ &.accordion-collapsed:hover {
+ background: transparent!important;
+ }
+
+ *[slot=header] {
+ cursor: pointer;
+ }
+
+ *[slot=content] ion-item {
+ --background: transparent!important;
+ }
+ // &.accordion-expanded ion-item[slot='header'] {
+ // --color: red;
+ // }
+}
+
+ul.baseList {
+ list-style: decimal;
+ padding: 0 0 0 1rem;
+
+ li {
+ margin: 0.85rem 0;
+ }
+}
+
.verticalLineBefore::before {
content: '';
position: absolute;
@@ -664,7 +843,7 @@ div.MuiScopedCssBaseline-root > .MuiBox-root > .MuiBox-root {
backdrop-filter: none;
}
-div[id^="widget-relative-container"]{
+body.dark div[id^="widget-relative-container"]{
box-shadow: 0 0 12px 0px rgba(0, 0, 0, 0.25);
}
@@ -685,6 +864,19 @@ div.MuiScopedCssBaseline-root {
}
}
+#onramp__frame {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ border: none;
+ position: relative;
+ bottom: -1rem;
+}
+
+.flex {
+ display: flex;
+}
.transition {
transition: all 85ms ease-in-out;
}
@@ -693,4 +885,118 @@ div.MuiScopedCssBaseline-root {
}
.opacity-100 {
opacity: 1!important;
+}
+.cursorPointer {
+ cursor: pointer;
+}
+.fontWeight300 {
+ font-weight: 300;
+}
+
+
+
+.plt-mobileweb, .plt-mobile {
+ ion-grid:has(.marketFilters) {
+ position: sticky;
+ top: -20px;
+ background: var(--ion-background-color);
+ z-index: 1;
+ }
+ div[id^=widget-relative-container] {
+ box-shadow: none;
+ }
+
+ .modalPage ion-content {
+ --background: var(--ion-background-color);
+
+ .widgetWrapper {
+ border: none;
+ box-shadow: none;
+ -webkit-backdrop-filter: none;
+ backdrop-filter: none;
+ background: transparent;
+
+ > ion-col {
+ text-align: center;
+ }
+ }
+
+ }
+
+ div[id^="widget-relative-container"] {
+ border: none;
+ }
+
+ div.MuiScopedCssBaseline-root {
+ background: transparent!important;
+ -webkit-backdrop-filter: none!important;
+ backdrop-filter: none!important;
+
+ >div[id^="widget-scrollable-container"] {
+ -webkit-backdrop-filter: none!important;
+ backdrop-filter: none!important;
+ }
+ }
+}
+
+.mobileConentModal{
+ --background: var(--ion-background-color);
+}
+#reader-scan-element {
+ video {
+ height: 100%;
+ width: auto!important;
+ }
+}
+
+.ErrorBoundary {
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ background: var(--ion-background-color);
+ color: var(--ion-text-color);
+ font-family: $font-family;
+ font-size: 1.5rem;
+ line-height: 1.5rem;
+ text-align: center;
+ padding: 1rem;
+}
+
+.itemInputContainter,
+.InputAssetWithDropDown {
+ > ion-row {
+ background: var(--item-background-shader);
+
+ padding: 0.65rem 0.5rem;
+ border-radius: 24px;
+ margin-bottom: 0.5rem;
+ }
+}
+
+
+.PublicationItem {
+ cursor: pointer;
+ transition: all 125ms ease-in-out;
+ overflow: hidden;
+
+ ion-img {
+ position: relative;
+ }
+
+ &:hover {
+ ion-img::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(var(--ion-color-primary-rgb), 0.5);
+ }
+ ion-text {
+ opacity: 0.6;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/styles/variables.scss b/src/styles/variables.scss
index dd5085b0..12170d01 100755
--- a/src/styles/variables.scss
+++ b/src/styles/variables.scss
@@ -28,12 +28,6 @@
--ion-color-gradient-tint: linear-gradient(270deg, var(--ion-color-secondary-tint),var(--ion-color-primary-tint));
--ion-color-gradient-text: linear-gradient(to right, rgb(var(--ion-color-primary-rgb)) , rgb(var(--ion-color-secondary-rgb)));
- .ion-color-gradient-text {
- background: var(--ion-color-gradient-text);
- -webkit-background-clip: text;
- background-clip: text;
- -webkit-text-fill-color: transparent;
- }
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45,211,111;
@@ -77,7 +71,18 @@
--ion-background-color: #f5f8ff;
--ion-background-color-rgb: 245,248,255;
- --background-gradient: linear-gradient(125deg,#182449, rgba(61, 158, 255, 0.2));
+ --background-gradient: #e6eefe;
+ --background-shader: #dee6ff;
+
+ --ion-text-color: #180d68;
+ --ion-text-color-rgb: 24, 13, 104;
+
+ --ion-color-dark: #180d68;
+ --ion-color-dark-rgb: 24, 13, 104;
+
+ --item-background: transparent;
+ --item-background-shader: #e2e9ff;
+ --item-background-shader-rgb: 224, 232, 255;
/* OnBoarding */
--account-center-position-top: -3px;
@@ -103,6 +108,13 @@
--ion-color-tint: var(--ion-color-gradient-tint);
}
+.ion-color-gradient-text {
+ background: var(--ion-color-gradient-text);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
/*
* Dark Colors
* -------------------------------------------
@@ -178,6 +190,14 @@ body.dark {
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
+
+ --background-gradient: linear-gradient(125deg,#182449, rgba(61, 158, 255, 0.2));
+ --background-shader: #182449;
+
+ --item-background: transparent;
+ --item-background-shader: #0f1629;
+ --item-background-shader-rgb: 15, 22, 41;
+
}
/*
diff --git a/src/utils/currency-format.ts b/src/utils/currency-format.ts
deleted file mode 100644
index 37cdb071..00000000
--- a/src/utils/currency-format.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-
-export const currencyFormat = (
- num: number,
- ops?: { currency?: string; language?: string }
-) => {
- const currency = ops?.currency || "USD";
- const language = ops?.language || "en-US";
- return num.toLocaleString(language, {
- style: "currency",
- currency,
- });
-}
\ No newline at end of file
diff --git a/src/utils/currencyFormat.ts b/src/utils/currencyFormat.ts
new file mode 100644
index 00000000..6bfe3278
--- /dev/null
+++ b/src/utils/currencyFormat.ts
@@ -0,0 +1,14 @@
+
+// format number to US dollar
+export const USDollar = new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD',
+});
+
+export const currencyFormat = new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD',
+ compactDisplay: 'short',
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+});
\ No newline at end of file
diff --git a/src/utils/getCoingeekoTokenId.ts b/src/utils/getCoingeekoTokenId.ts
new file mode 100644
index 00000000..e5006fa8
--- /dev/null
+++ b/src/utils/getCoingeekoTokenId.ts
@@ -0,0 +1,19 @@
+
+export const getCoingeekoTokenId = async (symbol: string) => {
+ // convert symbol to coingeeko id
+ const responseList = localStorage.getItem('hexa-lite-coingeeko/coinList');
+ let data;
+ if (responseList) {
+ data = JSON.parse(responseList);
+ } else {
+ const fetchResponse = await fetch(`https://api.coingecko.com/api/v3/coins/list`);
+ data = await fetchResponse.json();
+ localStorage.setItem('hexa-lite-coingeeko/coinList', JSON.stringify(data));
+ }
+ const coin: {id?: string} = data.find(
+ (c: any) =>
+ c.symbol.toLowerCase() === symbol.toLowerCase()
+ && !c.name.toLowerCase().includes('bridged')
+ );
+ return coin?.id;
+}
\ No newline at end of file
diff --git a/src/utils/getTokenHistoryPrice.ts b/src/utils/getTokenHistoryPrice.ts
new file mode 100644
index 00000000..63923370
--- /dev/null
+++ b/src/utils/getTokenHistoryPrice.ts
@@ -0,0 +1,33 @@
+
+export const getTokenHistoryPrice = async (symbol: string) => {
+ // convert symbol to coingeeko id
+ const responseList = localStorage.getItem('hexa-lite-coingeeko/coinList');
+ let data;
+ if (responseList) {
+ data = JSON.parse(responseList);
+ } else {
+ const fetchResponse = await fetch(`https://api.coingecko.com/api/v3/coins/list`);
+ data = await fetchResponse.json();
+ localStorage.setItem('hexa-lite-coingeeko/coinList', JSON.stringify(data));
+ }
+ const coin = data.find((c: any) => c.symbol.toLowerCase() === symbol.toLowerCase());
+ if (!coin) return [];
+
+ const responseToken = localStorage.getItem(`hexa-lite-coingeeko/coin/${coin.id}/market_chart`);
+ const jsonData = JSON.parse(responseToken||'{}');
+ const isDeadlineReach = (Date.now() - jsonData.timestamp) > (60 * 1000 * 30);
+ let tokenMarketData;
+ if (responseToken && !isDeadlineReach && jsonData.data) {
+ tokenMarketData = jsonData.data;
+ } else {
+ const url = `https://api.coingecko.com/api/v3/coins/${coin.id}/market_chart?vs_currency=usd&days=30&interval=daily`;
+ const res = await fetch(url);
+ const result = await res.json();
+ tokenMarketData = result?.prices as number[]||[];
+ localStorage.setItem(`hexa-lite-coingeeko/coin/${coin.id}/market_chart`, JSON.stringify({
+ data: tokenMarketData,
+ timestamp: Date.now()
+ }));
+ }
+ return tokenMarketData;
+}
\ No newline at end of file
diff --git a/src/utils/getTokenInfo.ts b/src/utils/getTokenInfo.ts
new file mode 100644
index 00000000..53d97ab2
--- /dev/null
+++ b/src/utils/getTokenInfo.ts
@@ -0,0 +1,71 @@
+import { getCoingeekoTokenId } from "./getCoingeekoTokenId";
+
+export type TokenInfo = {
+ description: {en: string};
+ categories: string[];
+ image: {
+ thumb: string;
+ small: string;
+ large: string;
+ };
+ market_data: {
+ ath: {usd: number};
+ ath_change_percentage: {usd: number};
+ ath_date: { usd: string };
+ atl: {usd: number};
+ atl_change_percentage: {usd: number};
+ atl_date: { usd: string };
+ circulating_supply: number;
+ current_price: { usd: number };
+ fully_diluted_valuation: { usd: number };
+ high_24h: { usd: number };
+ last_updated: string;
+ low_24h: { usd: number };
+ market_cap: { usd: number };
+ market_cap_change_24h: number;
+ market_cap_change_24h_in_currency: { usd: number };
+ market_cap_change_percentage_24h: number;
+ market_cap_change_percentage_24h_in_currency: { usd: number };
+ market_cap_fdv_ratio: number;
+ market_cap_rank: number;
+ max_supply: number;
+ price_change_24h: number;
+ price_change_24h_in_currency: { usd: number };
+ price_change_percentage_1h_in_currency: { usd: number };
+ price_change_percentage_1y_in_currency: { usd: number };
+ price_change_percentage_7d_in_currency: { usd: number };
+ price_change_percentage_14d_in_currency: { usd: number };
+ price_change_percentage_24h_in_currency: { usd: number };
+ price_change_percentage_30d_in_currency: { usd: number };
+ price_change_percentage_60d_in_currency: { usd: number };
+ price_change_percentage_200d_in_currency: { usd: number };
+ total_supply: number;
+ total_value_locked: number|null;
+ total_volume: { usd: number };
+ };
+ sentiment_votes_down_percentage: number;
+ sentiment_votes_up_percentage: number;
+
+};
+
+export const getTokenInfo = async (symbol: string) => {
+ const tokenId = await getCoingeekoTokenId(symbol);
+ if (!tokenId) return undefined;
+ // check localstorage if data is stored from less than 1 day
+ const response = localStorage.getItem(`hexa-lite-coingeeko/coin/${tokenId}/info`);
+ const jsonData = JSON.parse(response||'{}');
+ const isDeadlineReach = (Date.now() - jsonData.timestamp) > (60 * 1000 * 60 * 24);
+ let tokenInfo;
+ if (response && !isDeadlineReach && jsonData.data) {
+ tokenInfo = jsonData.data;
+ } else {
+ // fetch data from coingecko
+ tokenInfo = await fetch(`https://api.coingecko.com/api/v3/coins/${tokenId}?market_data=true&community_data=true`)
+ .then((res) => res.json());
+ localStorage.setItem(`hexa-lite-coingeeko/coin/${tokenId}/info`, JSON.stringify({
+ data: tokenInfo,
+ timestamp: Date.now()
+ }));
+ }
+ return tokenInfo as TokenInfo;
+}
\ No newline at end of file
diff --git a/src/utils/isStableAsset.ts b/src/utils/isStableAsset.ts
new file mode 100644
index 00000000..cfdeeae9
--- /dev/null
+++ b/src/utils/isStableAsset.ts
@@ -0,0 +1,20 @@
+export const isStableAsset = (symbol: string) => {
+ const stableAssets = [
+ "USDC",
+ "USDT",
+ "DAI",
+ "BUSD",
+ "TUSD",
+ "sUSD",
+ "USDbC",
+ "FRAX",
+ "LUSD",
+ "PYUSD",
+ "crvUSD",
+ "FDUSD",
+ "GHO",
+ ];
+ return stableAssets
+ .map((a) => a.toLocaleLowerCase())
+ .includes(symbol.toLocaleLowerCase());
+};
diff --git a/src/utils/numberFormat.ts b/src/utils/numberFormat.ts
new file mode 100644
index 00000000..b7c132b5
--- /dev/null
+++ b/src/utils/numberFormat.ts
@@ -0,0 +1,6 @@
+
+export const numberFormat = new Intl.NumberFormat('en-US', {
+ style: 'decimal',
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+});
\ No newline at end of file