diff --git a/public/widget/widget-execution-dark.png b/public/widget/widget-execution-dark.png
new file mode 100644
index 000000000..f12429d0b
Binary files /dev/null and b/public/widget/widget-execution-dark.png differ
diff --git a/public/widget/widget-execution-light.png b/public/widget/widget-execution-light.png
new file mode 100644
index 000000000..245167627
Binary files /dev/null and b/public/widget/widget-execution-light.png differ
diff --git a/public/widget/widget-quotes-dark.png b/public/widget/widget-quotes-dark.png
new file mode 100644
index 000000000..3ed1ece64
Binary files /dev/null and b/public/widget/widget-quotes-dark.png differ
diff --git a/public/widget/widget-quotes-light.png b/public/widget/widget-quotes-light.png
new file mode 100644
index 000000000..ee3dbbdf4
Binary files /dev/null and b/public/widget/widget-quotes-light.png differ
diff --git a/public/widget/widget-review-bridge-dark.png b/public/widget/widget-review-bridge-dark.png
new file mode 100644
index 000000000..854bc4c54
Binary files /dev/null and b/public/widget/widget-review-bridge-dark.png differ
diff --git a/public/widget/widget-review-bridge-light.png b/public/widget/widget-review-bridge-light.png
new file mode 100644
index 000000000..f3549a6bc
Binary files /dev/null and b/public/widget/widget-review-bridge-light.png differ
diff --git a/public/widget/widget-selection-dark-raw.png b/public/widget/widget-selection-dark-raw.png
new file mode 100644
index 000000000..ff2ab7871
Binary files /dev/null and b/public/widget/widget-selection-dark-raw.png differ
diff --git a/public/widget/widget-selection-dark.png b/public/widget/widget-selection-dark.png
new file mode 100644
index 000000000..ebe14a7d6
Binary files /dev/null and b/public/widget/widget-selection-dark.png differ
diff --git a/public/widget/widget-selection-light-raw.png b/public/widget/widget-selection-light-raw.png
new file mode 100644
index 000000000..abb61ee9e
Binary files /dev/null and b/public/widget/widget-selection-light-raw.png differ
diff --git a/public/widget/widget-selection-light.png b/public/widget/widget-selection-light.png
new file mode 100644
index 000000000..9762f9a8a
Binary files /dev/null and b/public/widget/widget-selection-light.png differ
diff --git a/public/widget/widget-success-dark.png b/public/widget/widget-success-dark.png
new file mode 100644
index 000000000..8fa739505
Binary files /dev/null and b/public/widget/widget-success-dark.png differ
diff --git a/public/widget/widget-success-light.png b/public/widget/widget-success-light.png
new file mode 100644
index 000000000..03402ecf8
Binary files /dev/null and b/public/widget/widget-success-light.png differ
diff --git a/src/app/api/widget-execution/route.tsx b/src/app/api/widget-execution/route.tsx
new file mode 100644
index 000000000..1c3682477
--- /dev/null
+++ b/src/app/api/widget-execution/route.tsx
@@ -0,0 +1,133 @@
+/* eslint-disable @next/next/no-img-element */
+
+/**
+ * Image Generation of Widget for SEO pages
+ * Step 4 - Route execution
+ *
+ * Example:
+ * ```
+ * http://localhost:3000/api/widget-execution?fromToken=0x0000000000000000000000000000000000000000&fromChainId=137&toToken=0x0000000000000000000000000000000000000000&toChainId=42161&amount=10&&theme=light&isSwap=true
+ * ```
+ *
+ * @typedef {Object} SearchParams
+ * @property {string} fromToken - The token address to send from.
+ * @property {number} fromChainId - The chain ID to send from.
+ * @property {string} toToken - The token address to send to.
+ * @property {number} toChainId - The chain ID to send to.
+ * @property {number} amount - The amount of tokens.
+ * @property {number} [amountUSD] - The USD equivalent amount (optional).
+ * @property {boolean} [isSwap] - True if transaction is a swap, default and false if transaction is a bridge (optional).
+ * @property {'light'|'dark'} [theme] - The theme for the widget (optional).
+ * @property {'from'|'to'|'amount'} [highlighted] - The highlighted element (optional).
+ *
+ */
+
+import type { ChainId } from '@lifi/sdk';
+import { ChainType, getChains, getToken } from '@lifi/sdk';
+import { ImageResponse } from 'next/og';
+import type { HighlightedAreas } from 'src/components/ImageGeneration/ImageGeneration.types';
+import { imageResponseOptions } from 'src/components/ImageGeneration/imageResponseOptions';
+import WidgetExecutionSSR from 'src/components/ImageGeneration/WidgetExecutionSSR';
+
+const WIDGET_IMAGE_WIDTH = 416;
+const WIDGET_IMAGE_HEIGHT = 432;
+const WIDGET_IMAGE_SCALING_FACTOR = 2;
+
+export async function GET(request: Request) {
+ const { searchParams } = new URL(request.url);
+ const amount = searchParams.get('amount');
+ const fromToken = searchParams.get('fromToken');
+ const fromChainId = searchParams.get('fromChainId');
+ const toToken = searchParams.get('toToken');
+ const toChainId = searchParams.get('toChainId');
+ const highlighted = searchParams.get('highlighted');
+ const theme = searchParams.get('theme');
+ const isSwap = searchParams.get('isSwap');
+
+ // Fetch chain data asynchronously before rendering
+ const getChainData = async (chainId: ChainId) => {
+ const chainsData = await getChains({
+ chainTypes: [ChainType.EVM, ChainType.SVM],
+ });
+ return chainsData.find((chainEl) => chainEl.id === chainId);
+ };
+
+ // Fetch from and to chain details (await this before rendering)
+ const fromChain = fromChainId
+ ? await getChainData(parseInt(fromChainId) as ChainId)
+ : null;
+ const toChain = toChainId
+ ? await getChainData(parseInt(toChainId) as ChainId)
+ : null;
+
+ // Fetch token asynchronously
+ const fetchToken = async (chainId: ChainId | null, token: string | null) => {
+ if (!chainId || !token) {
+ return null;
+ }
+ try {
+ const fetchedToken = await getToken(chainId, token);
+ return fetchedToken;
+ } catch (error) {
+ console.error('Error fetching token:', error);
+ return null;
+ }
+ };
+
+ const fromTokenData =
+ fromChainId && fromToken
+ ? await fetchToken(parseInt(fromChainId) as ChainId, fromToken)
+ : null;
+ const toTokenData =
+ toChainId && toToken
+ ? await fetchToken(parseInt(toChainId) as ChainId, toToken)
+ : null;
+
+ const options = await imageResponseOptions({
+ width: WIDGET_IMAGE_WIDTH,
+ height: WIDGET_IMAGE_HEIGHT,
+ scalingFactor: WIDGET_IMAGE_SCALING_FACTOR,
+ });
+
+ return new ImageResponse(
+ (
+
+
+
+
+ ),
+ options,
+ );
+}
diff --git a/src/app/api/widget-quotes/route.tsx b/src/app/api/widget-quotes/route.tsx
new file mode 100644
index 000000000..acd657978
--- /dev/null
+++ b/src/app/api/widget-quotes/route.tsx
@@ -0,0 +1,142 @@
+/* eslint-disable @next/next/no-img-element */
+
+/**
+ * Image Generation of Widget for SEO pages
+ * Step 2 - Quotes
+ *
+ * Example:
+ * ```
+ * http://localhost:3000/api/widget-quotes?fromToken=0x0000000000000000000000000000000000000000&fromChainId=137&toToken=0x0000000000000000000000000000000000000000&toChainId=42161&amount=10&highlighted=0&theme=light
+ * ```
+ *
+ * @typedef {Object} SearchParams
+ * @property {string} fromToken - The token address to send from.
+ * @property {number} fromChainId - The chain ID to send from.
+ * @property {string} toToken - The token address to send to.
+ * @property {number} toChainId - The chain ID to send to.
+ * @property {number} amount - The amount of tokens.
+ * @property {number} [amountUSD] - The USD equivalent amount (optional).
+ * @property {boolean} [isSwap] - True if transaction is a swap, default and false if transaction is a bridge (optional).
+ * @property {'light'|'dark'} [theme] - The theme for the widget (optional).
+ * @property {'from'|'to'|'amount'|'0'|'1'|'2'} [highlighted] - The highlighted element, numbers refer to quote index (optional).
+ *
+ */
+import type { ChainId } from '@lifi/sdk';
+import { ChainType, getChains, getToken } from '@lifi/sdk';
+import { ImageResponse } from 'next/og';
+import type { HighlightedAreas } from 'src/components/ImageGeneration/ImageGeneration.types';
+import { imageResponseOptions } from 'src/components/ImageGeneration/imageResponseOptions';
+import WidgetQuoteSSR from 'src/components/ImageGeneration/WidgetQuotesSSR';
+
+const WIDGET_IMAGE_WIDTH = 856;
+const WIDGET_IMAGE_HEIGHT = 490; //376;
+const WIDGET_IMAGE_SCALING_FACTOR = 2;
+
+export async function GET(request: Request) {
+ // console.time('start-time');
+ const { searchParams } = new URL(request.url);
+ const amount = searchParams.get('amount');
+ const amountUSD = searchParams.get('amountUSD');
+ const fromToken = searchParams.get('fromToken');
+ const fromChainId = searchParams.get('fromChainId');
+ const toToken = searchParams.get('toToken');
+ const toChainId = searchParams.get('toChainId');
+ const highlighted = searchParams.get('highlighted');
+ const theme = searchParams.get('theme');
+ const isSwap = searchParams.get('isSwap');
+
+ // Fetch chain data asynchronously before rendering
+ const getChainData = async (chainId: ChainId) => {
+ const chainsData = await getChains({
+ chainTypes: [ChainType.EVM, ChainType.SVM],
+ });
+ return chainsData.find((chainEl) => chainEl.id === chainId);
+ };
+
+ // Fetch from and to chain details (await this before rendering)
+ const fromChain = fromChainId
+ ? await getChainData(parseInt(fromChainId) as ChainId)
+ : null;
+ const toChain = toChainId
+ ? await getChainData(parseInt(toChainId) as ChainId)
+ : null;
+
+ // Fetch token asynchronously
+ const fetchToken = async (chainId: ChainId | null, token: string | null) => {
+ if (!chainId || !token) {
+ return null;
+ }
+ try {
+ const fetchedToken = await getToken(chainId, token);
+ return fetchedToken;
+ } catch (error) {
+ console.error('Error fetching token:', error);
+ return null;
+ }
+ };
+
+ const fromTokenData =
+ fromChainId && fromToken
+ ? await fetchToken(parseInt(fromChainId) as ChainId, fromToken)
+ : null;
+ const toTokenData =
+ toChainId && toToken
+ ? await fetchToken(parseInt(toChainId) as ChainId, toToken)
+ : null;
+
+ const routeAmount =
+ (parseFloat(fromTokenData?.priceUSD || '0') * parseFloat(amount || '0')) /
+ parseFloat(toTokenData?.priceUSD || '0');
+
+ const options = await imageResponseOptions({
+ width: WIDGET_IMAGE_WIDTH,
+ height: WIDGET_IMAGE_HEIGHT,
+ scalingFactor: WIDGET_IMAGE_SCALING_FACTOR,
+ });
+
+ const ImageResp = new ImageResponse(
+ (
+
+
+
+
+ ),
+ options,
+ );
+ // console.timeEnd('start-time');
+ return ImageResp;
+}
diff --git a/src/app/api/widget-review/route.tsx b/src/app/api/widget-review/route.tsx
new file mode 100644
index 000000000..04f11c874
--- /dev/null
+++ b/src/app/api/widget-review/route.tsx
@@ -0,0 +1,137 @@
+/* eslint-disable @next/next/no-img-element */
+
+/**
+ * Image Generation of Widget for SEO pages
+ * Step 3 - Review quote
+ *
+ * Example:
+ * ```
+ * http://localhost:3000/api/widget-review?fromToken=0x0000000000000000000000000000000000000000&fromChainId=137&toToken=0x0000000000000000000000000000000000000000&toChainId=42161&amount=10&highlighted=amount&theme=dark
+ * ```
+ *
+ * @typedef {Object} SearchParams
+ * @property {string} fromToken - The token address to send from.
+ * @property {number} fromChainId - The chain ID to send from.
+ * @property {string} toToken - The token address to send to.
+ * @property {number} toChainId - The chain ID to send to.
+ * @property {number} amount - The amount of tokens.
+ * @property {number} [amountUSD] - The USD equivalent amount (optional).
+ * @property {boolean} [isSwap] - True if transaction is a swap, default and false if transaction is a bridge (optional).
+ * @property {'light'|'dark'} [theme] - The theme for the widget (optional).
+ * @property {'from'|'to'|'amount'} [highlighted] - The highlighted element (optional).
+ *
+ */
+
+import type { ChainId } from '@lifi/sdk';
+import { ChainType, getChains, getToken } from '@lifi/sdk';
+import { ImageResponse } from 'next/og';
+import type { HighlightedAreas } from 'src/components/ImageGeneration/ImageGeneration.types';
+import { imageResponseOptions } from 'src/components/ImageGeneration/imageResponseOptions';
+import WidgetReviewSSR from 'src/components/ImageGeneration/WidgetReviewSSR';
+
+const WIDGET_IMAGE_WIDTH = 416;
+const WIDGET_IMAGE_HEIGHT = 440;
+const WIDGET_IMAGE_SCALING_FACTOR = 2;
+
+export async function GET(request: Request) {
+ const { searchParams } = new URL(request.url);
+ const amount = searchParams.get('amount');
+ const amountUSD = searchParams.get('amountUSD');
+ const fromToken = searchParams.get('fromToken');
+ const fromChainId = searchParams.get('fromChainId');
+ const toToken = searchParams.get('toToken');
+ const toChainId = searchParams.get('toChainId');
+ const highlighted = searchParams.get('highlighted');
+ const theme = searchParams.get('theme');
+ const isSwap = searchParams.get('isSwap');
+
+ // Fetch chain data asynchronously before rendering
+ const getChainData = async (chainId: ChainId) => {
+ const chainsData = await getChains({
+ chainTypes: [ChainType.EVM, ChainType.SVM],
+ });
+ return chainsData.find((chainEl) => chainEl.id === chainId);
+ };
+
+ // Fetch from and to chain details (await this before rendering)
+ const fromChain = fromChainId
+ ? await getChainData(parseInt(fromChainId) as ChainId)
+ : null;
+ const toChain = toChainId
+ ? await getChainData(parseInt(toChainId) as ChainId)
+ : null;
+
+ // Fetch token asynchronously
+ const fetchToken = async (chainId: ChainId | null, token: string | null) => {
+ if (!chainId || !token) {
+ return null;
+ }
+ try {
+ const fetchedToken = await getToken(chainId, token);
+ return fetchedToken;
+ } catch (error) {
+ console.error('Error fetching token:', error);
+ return null;
+ }
+ };
+
+ const fromTokenData =
+ fromChainId && fromToken
+ ? await fetchToken(parseInt(fromChainId) as ChainId, fromToken)
+ : null;
+ const toTokenData =
+ toChainId && toToken
+ ? await fetchToken(parseInt(toChainId) as ChainId, toToken)
+ : null;
+
+ const routeAmount =
+ (parseFloat(fromTokenData?.priceUSD || '0') * parseFloat(amount || '0')) /
+ parseFloat(toTokenData?.priceUSD || '0');
+
+ const options = await imageResponseOptions({
+ width: WIDGET_IMAGE_WIDTH,
+ height: WIDGET_IMAGE_HEIGHT,
+ scalingFactor: WIDGET_IMAGE_SCALING_FACTOR,
+ });
+ return new ImageResponse(
+ (
+
+
+
+
+ ),
+ options,
+ );
+}
diff --git a/src/app/api/widget-selection/route.tsx b/src/app/api/widget-selection/route.tsx
new file mode 100644
index 000000000..4e4b613ea
--- /dev/null
+++ b/src/app/api/widget-selection/route.tsx
@@ -0,0 +1,132 @@
+/* eslint-disable @next/next/no-img-element */
+
+/**
+ * Image Generation of Widget for SEO pages
+ * Step 1 - Selecting Tokens
+ *
+ * Example:
+ * ```
+ * http://localhost:3000/api/widget-selection?fromToken=0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063&fromChainId=137&toToken=0xdAC17F958D2ee523a2206206994597C13D831ec7&toChainId=1&amount=3&theme=dark
+ * ```
+ *
+ * @typedef {Object} SearchParams
+ * @property {string} fromToken - The token address to send from.
+ * @property {number} fromChainId - The chain ID to send from.
+ * @property {string} toToken - The token address to send to.
+ * @property {number} toChainId - The chain ID to send to.
+ * @property {number} amount - The amount of tokens.
+ * @property {number} [amountUSD] - The USD equivalent amount (optional).
+ * @property {'light'|'dark'} [theme] - The theme for the widget (optional).
+ * @property {'from'|'to'|'amount'} [highlighted] - The highlighted element (optional).
+ *
+ */
+
+import type { ChainId } from '@lifi/sdk';
+import { ChainType, getChains, getToken } from '@lifi/sdk';
+import { ImageResponse } from 'next/og';
+import type { HighlightedAreas } from 'src/components/ImageGeneration/ImageGeneration.types';
+import { imageResponseOptions } from 'src/components/ImageGeneration/imageResponseOptions';
+import WidgetImageSSR from 'src/components/ImageGeneration/WidgetImageSSR';
+
+const WIDGET_IMAGE_WIDTH = 416;
+const WIDGET_IMAGE_HEIGHT = 496;
+const WIDGET_IMAGE_SCALING_FACTOR = 2;
+
+export async function GET(request: Request) {
+ const { searchParams } = new URL(request.url);
+ const amount = searchParams.get('amount');
+ const amountUSD = searchParams.get('amountUSD');
+ const fromToken = searchParams.get('fromToken');
+ const fromChainId = searchParams.get('fromChainId');
+ const toToken = searchParams.get('toToken');
+ const toChainId = searchParams.get('toChainId');
+ const highlighted = searchParams.get('highlighted');
+ const theme = searchParams.get('theme');
+
+ // Fetch chain data asynchronously before rendering
+ const getChainData = async (chainId: ChainId) => {
+ const chainsData = await getChains({
+ chainTypes: [ChainType.EVM, ChainType.SVM],
+ });
+ return chainsData.find((chainEl) => chainEl.id === chainId);
+ };
+
+ // Fetch from and to chain details (await this before rendering)
+ const fromChain = fromChainId
+ ? await getChainData(parseInt(fromChainId) as ChainId)
+ : null;
+ const toChain = toChainId
+ ? await getChainData(parseInt(toChainId) as ChainId)
+ : null;
+
+ // Fetch token asynchronously
+ const fetchToken = async (chainId: ChainId | null, token: string | null) => {
+ if (!chainId || !token) {
+ return null;
+ }
+ try {
+ const fetchedToken = await getToken(chainId, token);
+ return fetchedToken;
+ } catch (error) {
+ console.error('Error fetching token:', error);
+ return null;
+ }
+ };
+
+ const fromTokenData =
+ fromChainId && fromToken
+ ? await fetchToken(parseInt(fromChainId) as ChainId, fromToken)
+ : null;
+ const toTokenData =
+ toChainId && toToken
+ ? await fetchToken(parseInt(toChainId) as ChainId, toToken)
+ : null;
+
+ const options = await imageResponseOptions({
+ width: WIDGET_IMAGE_WIDTH,
+ height: WIDGET_IMAGE_HEIGHT,
+ scalingFactor: WIDGET_IMAGE_SCALING_FACTOR,
+ });
+
+ return new ImageResponse(
+ (
+
+
+
{' '}
+
+ ),
+ options,
+ );
+}
diff --git a/src/app/api/widget-success/route.tsx b/src/app/api/widget-success/route.tsx
new file mode 100644
index 000000000..5aa0064d6
--- /dev/null
+++ b/src/app/api/widget-success/route.tsx
@@ -0,0 +1,115 @@
+/* eslint-disable @next/next/no-img-element */
+
+/**
+ * Image Generation of Widget for SEO pages
+ * Step 5 - Route success
+ *
+ * Example:
+ * ```
+ * http://localhost:3000/api/widget-success?toToken=0x0000000000000000000000000000000000000000&toChainId=42161&amount=10&&theme=light&isSwap=true
+ * ```
+ *
+ * @typedef {Object} SearchParams
+ * @property {string} toToken - The token address to send to.
+ * @property {number} toChainId - The chain ID to send to.
+ * @property {number} amount - The amount of tokens.
+ * @property {boolean} [isSwap] - True if transaction is a swap, default and false if transaction is a bridge (optional).
+ * @property {'light'|'dark'} [theme] - The theme for the widget (optional).
+ *
+ */
+
+import type { ChainId } from '@lifi/sdk';
+import { ChainType, getChains, getToken } from '@lifi/sdk';
+import { ImageResponse } from 'next/og';
+import { imageResponseOptions } from 'src/components/ImageGeneration/imageResponseOptions';
+import WidgetSuccessSSR from 'src/components/ImageGeneration/WidgetSuccessSSR';
+
+const WIDGET_IMAGE_WIDTH = 416;
+const WIDGET_IMAGE_HEIGHT = 432;
+const WIDGET_IMAGE_SCALING_FACTOR = 2;
+
+export async function GET(request: Request) {
+ const { searchParams } = new URL(request.url);
+ const amount = searchParams.get('amount');
+ const toToken = searchParams.get('toToken');
+ const toChainId = searchParams.get('toChainId');
+ const theme = searchParams.get('theme');
+ const isSwap = searchParams.get('isSwap');
+
+ // Fetch chain data asynchronously before rendering
+ const getChainData = async (chainId: ChainId) => {
+ const chainsData = await getChains({
+ chainTypes: [ChainType.EVM, ChainType.SVM],
+ });
+ return chainsData.find((chainEl) => chainEl.id === chainId);
+ };
+
+ // Fetch to chain details (await this before rendering)
+ const toChain = toChainId
+ ? await getChainData(parseInt(toChainId) as ChainId)
+ : null;
+
+ // Fetch token asynchronously
+ const fetchToken = async (chainId: ChainId | null, token: string | null) => {
+ if (!chainId || !token) {
+ return null;
+ }
+ try {
+ const fetchedToken = await getToken(chainId, token);
+ return fetchedToken;
+ } catch (error) {
+ console.error('Error fetching token:', error);
+ return null;
+ }
+ };
+
+ const toTokenData =
+ toChainId && toToken
+ ? await fetchToken(parseInt(toChainId) as ChainId, toToken)
+ : null;
+
+ const options = await imageResponseOptions({
+ width: WIDGET_IMAGE_WIDTH,
+ height: WIDGET_IMAGE_HEIGHT,
+ scalingFactor: WIDGET_IMAGE_SCALING_FACTOR,
+ });
+
+ return new ImageResponse(
+ (
+
+
+
+
+ ),
+ options,
+ );
+}
diff --git a/src/components/AvatarBadge/AvatarBadge.style.ts b/src/components/AvatarBadge/AvatarBadge.style.ts
new file mode 100644
index 000000000..b29dfec0d
--- /dev/null
+++ b/src/components/AvatarBadge/AvatarBadge.style.ts
@@ -0,0 +1,72 @@
+import { Avatar, Badge, Avatar as MuiAvatar } from '@mui/material';
+import { styled } from '@mui/material/styles';
+import type { BadgeOffsetProps } from './AvatarBadge';
+import { getAvatarMask } from './getAvatarMask';
+
+interface StyledAvatarProps {
+ avatarSize: number;
+ badgeSize: number;
+ badgeOffset?: BadgeOffsetProps;
+ badgeGap?: number;
+}
+
+// Styled Avatar component for the badge
+export const StyledAvatar = styled(Avatar, {
+ shouldForwardProp: (prop) =>
+ prop !== 'avatarSize' &&
+ prop !== 'badgeSize' &&
+ prop !== 'badgeOffset' &&
+ prop !== 'badgeGap',
+})(({ avatarSize, badgeSize, badgeOffset, badgeGap }) => ({
+ height: avatarSize,
+ width: avatarSize,
+ mask: getAvatarMask({ avatarSize, badgeSize, badgeOffset, badgeGap }), // Apply dynamic mask based on avatar and badge size
+ '> img': {
+ height: '100%',
+ width: '100%',
+ objectFit: 'contain',
+ },
+}));
+
+interface StyledBadgeProps {
+ badgeOffset?: BadgeOffsetProps;
+ avatarSize: number;
+}
+
+// Styled Badge component for the badge
+export const StyledBadge = styled(Badge, {
+ shouldForwardProp: (prop) => prop !== 'badgeOffset' && prop !== 'avatarSize',
+})(({ badgeOffset, avatarSize }) => ({
+ borderRadius: '50%',
+ display: 'block',
+ height: avatarSize,
+ width: avatarSize,
+
+ '.MuiBadge-badge': {
+ position: 'static',
+ transform: 'none',
+ top: 'unset',
+ right: 'unset',
+ zIndex: 'unset',
+ minWidth: 'unset',
+ padding: 'unset',
+ height: 'unset',
+ lineHeight: 'unset',
+ ...((badgeOffset?.x || badgeOffset?.y) && {
+ transform: `translate(${badgeOffset?.x ? badgeOffset.x : 0}px, ${badgeOffset?.y ? badgeOffset.y : 0}px)`,
+ }),
+ },
+}));
+
+// Styled avatar
+export const StyledBadgeAvatar = styled(MuiAvatar)<{
+ badgeSize: number;
+}>(({ badgeSize }) => ({
+ width: badgeSize,
+ height: badgeSize,
+ position: 'absolute',
+ bottom: 0,
+ right: 0,
+ top: 'unset',
+ left: 'unset',
+}));
diff --git a/src/components/AvatarBadge/AvatarBadge.tsx b/src/components/AvatarBadge/AvatarBadge.tsx
new file mode 100644
index 000000000..23792f16b
--- /dev/null
+++ b/src/components/AvatarBadge/AvatarBadge.tsx
@@ -0,0 +1,61 @@
+import { Skeleton } from '@mui/material';
+import React from 'react';
+import {
+ StyledAvatar,
+ StyledBadge,
+ StyledBadgeAvatar,
+} from './AvatarBadge.style';
+
+export interface BadgeOffsetProps {
+ x?: number;
+ y?: number;
+}
+
+type AvatarBadgeProps = {
+ avatarAlt: string;
+ avatarSize: number;
+ avatarSrc?: string;
+ badgeAlt: string;
+ badgeSize: number;
+ badgeSrc?: string;
+ badgeOffset?: BadgeOffsetProps;
+ badgeGap?: number;
+};
+
+const AvatarBadge: React.FC = ({
+ avatarAlt,
+ avatarSize,
+ avatarSrc,
+ badgeAlt,
+ badgeSize,
+ badgeSrc,
+ badgeOffset,
+ badgeGap,
+}) => {
+ return (
+
+
+
+ }
+ >
+
+
+
+
+ );
+};
+
+export default AvatarBadge;
diff --git a/src/components/AvatarBadge/SSR/AvatarBadgeSSR.tsx b/src/components/AvatarBadge/SSR/AvatarBadgeSSR.tsx
new file mode 100644
index 000000000..1dbc44205
--- /dev/null
+++ b/src/components/AvatarBadge/SSR/AvatarBadgeSSR.tsx
@@ -0,0 +1,79 @@
+/* eslint-disable jsx-a11y/alt-text */
+/* eslint-disable @next/next/no-img-element */
+import type { BadgeOffsetProps } from 'src/components/AvatarBadge/AvatarBadge';
+
+type AvatarBadgeSSRProps = {
+ avatarSrc?: string;
+ badgeSrc?: string;
+ badgeOffset?: BadgeOffsetProps;
+ avatarSize: number;
+ badgeGap?: number;
+ badgeSize: number;
+ theme?: 'light' | 'dark';
+};
+
+export const AvatarBadgeSSR = ({
+ avatarSrc,
+ badgeSrc,
+ badgeOffset,
+ badgeGap,
+ avatarSize,
+ badgeSize,
+ theme,
+}: AvatarBadgeSSRProps) => {
+ return (
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/components/AvatarBadge/getAvatarMask.ts b/src/components/AvatarBadge/getAvatarMask.ts
new file mode 100644
index 000000000..632cb00b7
--- /dev/null
+++ b/src/components/AvatarBadge/getAvatarMask.ts
@@ -0,0 +1,52 @@
+/**
+ * Generates a dynamic avatar mask that positions a badge based on the avatar size and optional badge gap/offset.
+ *
+ * The mask uses a radial gradient to blend the badge into the avatar, simulating the appearance of the badge
+ * partially overlapping the avatar's edge. The function also allows specifying a gap between the avatar and badge,
+ * along with custom x/y offsets for fine-tuning positioning.
+ *
+ * @param {number} avatarSize - The size of the avatar in pixels (e.g. 44, 32).
+ * @param {number} badgeSize - The size of the badge in pixels (e.g. 12).
+ * @param {BadgeOffsetProps} [badgeOffset] - Optional x and y offset values to adjust the badge's position.
+ * @param {number} [badgeGap] - Optional gap between the avatar and badge, defaults to a quarter of the badge size.
+ * @returns {string} The radial gradient mask for the avatar.
+ *
+ * The `badgeGap` introduces space between the avatar and badge by modifying the badge's radius.
+ * If `badgeGap` is not provided, a default value of 25% of the badge size is used.
+ *
+ * Example usage:
+ * ```
+ * getAvatarMask({
+ * avatarSize: 44,
+ * badgeSize: 12,
+ * badgeOffset: { x: 4, y: 4 },
+ * badgeGap: 2,
+ * });
+ * ```
+ */
+
+import type { BadgeOffsetProps } from './AvatarBadge';
+
+interface GetAvatarMask {
+ avatarSize: number;
+ badgeSize: number;
+ badgeOffset?: BadgeOffsetProps;
+ badgeGap?: number;
+}
+
+export const getAvatarMask = ({
+ avatarSize,
+ badgeSize,
+ badgeOffset,
+ badgeGap,
+}: GetAvatarMask) => {
+ const badgeRadius =
+ badgeGap !== undefined
+ ? (badgeSize + badgeGap) / 2
+ : (badgeSize + badgeSize / 4) / 2; // Badge radius with default gap if not provided
+ const badgeOffsetX =
+ avatarSize - badgeSize / 2 + (!!badgeOffset?.x ? badgeOffset?.x : 0);
+ const badgeOffsetY =
+ avatarSize - badgeSize / 2 + (!!badgeOffset?.y ? badgeOffset?.y : 0);
+ return `radial-gradient(circle ${badgeRadius}px at calc(${badgeOffsetX}px) calc(${badgeOffsetY}px), #fff0 96%, #fff) 100% 100% / 100% 100% no-repeat`;
+};
diff --git a/src/components/ImageGeneration/Field.tsx b/src/components/ImageGeneration/Field.tsx
new file mode 100644
index 000000000..7f3a514ff
--- /dev/null
+++ b/src/components/ImageGeneration/Field.tsx
@@ -0,0 +1,265 @@
+/* eslint-disable jsx-a11y/alt-text */
+/* eslint-disable @next/next/no-img-element */
+import type { ExtendedChain, Token } from '@lifi/sdk';
+import { AvatarBadgeSSR } from '../AvatarBadge/SSR/AvatarBadgeSSR';
+import { FieldSkeleton } from './FieldSkeleton';
+import type { ImageTheme } from './ImageGeneration.types';
+
+function formatDecimal(n: number): string | number {
+ // Check if the number is a whole number
+ if (Number.isInteger(n)) {
+ return n; // Return the number without decimals
+ }
+
+ // Convert to string to check the number of decimals
+ const decimalPart = n.toString().split('.')[1];
+
+ // If it has 2 or fewer decimal places, return it with 2 decimals
+ if (decimalPart && decimalPart.length <= 2) {
+ return n.toFixed(2);
+ }
+
+ // Otherwise, return the number rounded to 6 decimal places
+ return n.toFixed(6);
+}
+
+const Field = ({
+ sx,
+ token,
+ chain,
+ type,
+ amount = 0,
+ extendedHeight,
+ theme,
+ amountUSD,
+ routeAmount,
+ routeAmountUSD,
+ highlighted,
+ fullWidth,
+ showSkeletons,
+}: {
+ sx?: any; //SxProps;
+ token?: Token | null;
+ chain?: ExtendedChain | null;
+ theme?: ImageTheme;
+ type:
+ | 'amount'
+ | 'token'
+ | 'quote'
+ | 'review'
+ | 'quote-amount'
+ | 'amount-selection'
+ | 'button'
+ | 'title'
+ | 'card-title'
+ | 'success';
+ amount?: number | null;
+ amountUSD?: number | null;
+ highlighted?: boolean | null;
+ routeAmount?: number | null;
+ routeAmountUSD?: number | null;
+ extendedHeight?: boolean;
+ fullWidth?: boolean;
+ showSkeletons?: boolean;
+}) => {
+ // Function to calculate top offset based on conditions
+ const getOffset = () => {
+ if (type === 'amount') {
+ return 46;
+ }
+ if (type === 'quote') {
+ if (extendedHeight) {
+ return 56;
+ }
+ return 16;
+ } else {
+ return 46;
+ }
+ };
+ const getWidth = () => {
+ if (type === 'quote') {
+ return 315;
+ } else if (fullWidth) {
+ return 368;
+ } else {
+ return 174;
+ }
+ };
+
+ const containerOffset = getOffset();
+ const containerWidth = getWidth();
+
+ return (
+
+
+ {type !== 'button' && (
+
+ {token && chain && (
+
+ )}
+ {type === 'token' && (
+
+ )}
+ {type !== 'token' && (
+
+
+ {formatDecimal(routeAmount || amount || 0)}
+
+ {!showSkeletons && token ? (
+
+
+ ${(routeAmountUSD || amountUSD || amount || 0).toFixed(2)}
+
+ {type === 'review' && (
+
+ {`${token.symbol} on ${chain?.name}`}
+
+ )}
+
+ ) : (
+ <>
+ {type === 'review' && (
+
+ )}
+ {type === 'success' && (
+
+ )}
+ {type === 'quote' && (
+
+ )}
+ >
+ )}
+
+ )}
+
+ )}
+ {type === 'quote' && (
+
+ )}
+
+
+ );
+};
+
+export default Field;
diff --git a/src/components/ImageGeneration/FieldSkeleton.tsx b/src/components/ImageGeneration/FieldSkeleton.tsx
new file mode 100644
index 000000000..5ac1bb709
--- /dev/null
+++ b/src/components/ImageGeneration/FieldSkeleton.tsx
@@ -0,0 +1,26 @@
+/* eslint-disable jsx-a11y/alt-text */
+/* eslint-disable @next/next/no-img-element */
+import type { CSSProperties } from 'react';
+
+export const FieldSkeleton = ({
+ width,
+ height,
+ sx,
+}: {
+ width: number;
+ height: number;
+ sx?: CSSProperties;
+}) => {
+ return (
+
+ );
+};
diff --git a/src/components/ImageGeneration/ImageGeneration.types.ts b/src/components/ImageGeneration/ImageGeneration.types.ts
new file mode 100644
index 000000000..c76b8676f
--- /dev/null
+++ b/src/components/ImageGeneration/ImageGeneration.types.ts
@@ -0,0 +1,3 @@
+export type HighlightedAreas = 'from' | 'to' | 'amount';
+
+export type ImageTheme = 'light' | 'dark';
diff --git a/src/components/ImageGeneration/Label.tsx b/src/components/ImageGeneration/Label.tsx
new file mode 100644
index 000000000..c8c73aaa6
--- /dev/null
+++ b/src/components/ImageGeneration/Label.tsx
@@ -0,0 +1,103 @@
+/* eslint-disable jsx-a11y/alt-text */
+/* eslint-disable @next/next/no-img-element */
+
+import type { ImageTheme } from './ImageGeneration.types';
+
+const Label = ({
+ sx,
+ buttonLabel,
+ cardTitle,
+ cardContent,
+ title,
+ theme,
+ fullWidth,
+}: {
+ sx?: any; //SxProps;
+ cardTitle?: string;
+ cardContent?: string;
+ theme?: ImageTheme;
+ buttonLabel?: string;
+ title?: string;
+ fullWidth?: boolean;
+}) => {
+ return (
+ <>
+ {!!title && (
+
+ )}
+ {!!cardTitle && (
+
+ {cardTitle}
+
+ )}
+ {!!cardContent && (
+
+ {cardContent}
+
+ )}
+ {!!buttonLabel && (
+
+ )}
+ >
+ );
+};
+
+export default Label;
diff --git a/src/components/ImageGeneration/WidgetExecutionSSR.tsx b/src/components/ImageGeneration/WidgetExecutionSSR.tsx
new file mode 100644
index 000000000..1786b5750
--- /dev/null
+++ b/src/components/ImageGeneration/WidgetExecutionSSR.tsx
@@ -0,0 +1,129 @@
+import type { ExtendedChain, Token } from '@lifi/sdk';
+import WidgetFieldSSR from './Field';
+import { FieldSkeleton } from './FieldSkeleton';
+import type { HighlightedAreas, ImageTheme } from './ImageGeneration.types';
+import Label from './Label';
+
+const SCALING_FACTOR = 2;
+
+interface WidgetReviewSSRProps {
+ fromChain?: ExtendedChain | null;
+ toChain?: ExtendedChain | null;
+ fromToken?: Token | null;
+ toToken?: Token | null;
+ theme?: ImageTheme;
+ isSwap?: boolean;
+ amount?: string | null;
+ width: number;
+ height: number;
+ highlighted?: HighlightedAreas;
+}
+
+const WidgetExecutionSSR = ({
+ fromChain,
+ toChain,
+ theme,
+ fromToken,
+ isSwap,
+ toToken,
+ amount,
+ width,
+ height,
+ highlighted,
+}: WidgetReviewSSRProps) => {
+ return (
+
+
+ {
+ // pages container -->
+ }
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default WidgetExecutionSSR;
diff --git a/src/components/ImageGeneration/WidgetImageSSR.tsx b/src/components/ImageGeneration/WidgetImageSSR.tsx
new file mode 100644
index 000000000..f35cb77e1
--- /dev/null
+++ b/src/components/ImageGeneration/WidgetImageSSR.tsx
@@ -0,0 +1,97 @@
+// WidgetImageSSR.tsx
+
+import type { ExtendedChain, Token } from '@lifi/sdk';
+import WidgetFieldSSR from './Field';
+import type { HighlightedAreas, ImageTheme } from './ImageGeneration.types';
+
+const SCALING_FACTOR = 2;
+
+interface WidgetImageSSRProps {
+ fromChain?: ExtendedChain | null;
+ toChain?: ExtendedChain | null;
+ fromToken?: Token | null;
+ toToken?: Token | null;
+ amount?: string | null;
+ amountUSD?: string | null;
+ width: number;
+ height: number;
+ theme?: ImageTheme | null;
+ highlighted?: HighlightedAreas;
+}
+
+const WidgetImageSSR = ({
+ fromChain,
+ toChain,
+ fromToken,
+ toToken,
+ amount,
+ amountUSD,
+ width,
+ height,
+ theme,
+ highlighted,
+}: WidgetImageSSRProps) => {
+ return (
+
+ );
+};
+
+export default WidgetImageSSR;
diff --git a/src/components/ImageGeneration/WidgetQuotesSSR.tsx b/src/components/ImageGeneration/WidgetQuotesSSR.tsx
new file mode 100644
index 000000000..b0ed750f6
--- /dev/null
+++ b/src/components/ImageGeneration/WidgetQuotesSSR.tsx
@@ -0,0 +1,153 @@
+import type { ExtendedChain, Token } from '@lifi/sdk';
+import WidgetFieldSSR from './Field';
+import type { HighlightedAreas, ImageTheme } from './ImageGeneration.types';
+import Label from './Label';
+
+const SCALING_FACTOR = 2;
+
+interface WidgetQuoteSSRProps {
+ fromChain?: ExtendedChain | null;
+ toChain?: ExtendedChain | null;
+ fromToken?: Token | null;
+ toToken?: Token | null;
+ routeAmount?: number | null;
+ amount?: string | null;
+ isSwap?: boolean;
+ amountUSD?: string | null;
+ width: number;
+ height: number;
+ highlighted?: HighlightedAreas;
+ theme?: ImageTheme | null;
+}
+
+const WidgetQuoteSSR = ({
+ fromChain,
+ toChain,
+ fromToken,
+ toToken,
+ theme,
+ amount,
+ isSwap,
+ amountUSD,
+ routeAmount,
+ width,
+ height,
+ highlighted,
+}: WidgetQuoteSSRProps) => {
+ return (
+
+
+ {
+ // pages container -->
+ }
+
+
+
+
+ {Array(5)
+ .fill(0)
+ .map((_, index) => (
+
+ ))}
+
+
+
+
+
+ );
+};
+
+export default WidgetQuoteSSR;
diff --git a/src/components/ImageGeneration/WidgetReviewSSR.tsx b/src/components/ImageGeneration/WidgetReviewSSR.tsx
new file mode 100644
index 000000000..fd5e1de58
--- /dev/null
+++ b/src/components/ImageGeneration/WidgetReviewSSR.tsx
@@ -0,0 +1,120 @@
+import type { ExtendedChain, Token } from '@lifi/sdk';
+import WidgetFieldSSR from './Field';
+import { FieldSkeleton } from './FieldSkeleton';
+import type { HighlightedAreas, ImageTheme } from './ImageGeneration.types';
+import Label from './Label';
+
+const SCALING_FACTOR = 2;
+
+interface WidgetReviewSSRProps {
+ fromChain?: ExtendedChain | null;
+ toChain?: ExtendedChain | null;
+ fromToken?: Token | null;
+ toToken?: Token | null;
+ theme?: ImageTheme;
+ isSwap?: boolean;
+ amount?: string | null;
+ width: number;
+ height: number;
+ highlighted?: HighlightedAreas;
+}
+
+const WidgetReviewSSR = ({
+ fromChain,
+ toChain,
+ theme,
+ fromToken,
+ isSwap,
+ toToken,
+ amount,
+ width,
+ height,
+ highlighted,
+}: WidgetReviewSSRProps) => {
+ return (
+
+
+ {
+ // pages container -->
+ }
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default WidgetReviewSSR;
diff --git a/src/components/ImageGeneration/WidgetSuccessSSR.tsx b/src/components/ImageGeneration/WidgetSuccessSSR.tsx
new file mode 100644
index 000000000..f91fb8959
--- /dev/null
+++ b/src/components/ImageGeneration/WidgetSuccessSSR.tsx
@@ -0,0 +1,85 @@
+import type { ExtendedChain, Token } from '@lifi/sdk';
+import WidgetFieldSSR from './Field';
+import { FieldSkeleton } from './FieldSkeleton';
+import type { ImageTheme } from './ImageGeneration.types';
+import Label from './Label';
+
+const SCALING_FACTOR = 2;
+
+interface WidgetReviewSSRProps {
+ toChain?: ExtendedChain | null;
+ toToken?: Token | null;
+ theme?: ImageTheme;
+ isSwap?: boolean;
+ amount?: string | null;
+ width: number;
+ height: number;
+}
+
+const WidgetSuccessSSR = ({
+ toChain,
+ theme,
+ isSwap,
+ toToken,
+ amount,
+ width,
+ height,
+}: WidgetReviewSSRProps) => {
+ return (
+
+
+ {
+ // pages container -->
+ }
+
+
+
+
+
+
+
+
+ );
+};
+
+export default WidgetSuccessSSR;
diff --git a/src/components/ImageGeneration/imageResponseOptions.ts b/src/components/ImageGeneration/imageResponseOptions.ts
new file mode 100644
index 000000000..eb9d52702
--- /dev/null
+++ b/src/components/ImageGeneration/imageResponseOptions.ts
@@ -0,0 +1,57 @@
+import type { Font } from 'node_modules/next/dist/compiled/@vercel/og/satori';
+
+export const imageResponseOptions = async ({
+ width,
+ height,
+ scalingFactor,
+}: {
+ width: number;
+ height: number;
+ scalingFactor: number;
+}) => {
+ return {
+ headers: {
+ 'Cache-Control': `public, max-age=${60 * 60 * 1000 * 24}, immutable`,
+ },
+ width: width * scalingFactor,
+ height: height * scalingFactor,
+ fonts: await getFonts(), // Await properly within the async function
+ };
+};
+
+async function getFonts(): Promise {
+ // This is unfortunate but I can't figure out how to load local font files
+ // when deployed to vercel.
+ const [interRegular, interSemiBold, interBold] = await Promise.all([
+ fetch(`https://fonts.cdnfonts.com/s/19795/Inter-Regular.woff`).then((res) =>
+ res.arrayBuffer(),
+ ),
+ fetch(`https://fonts.cdnfonts.com/s/19795/Inter-SemiBold.woff`).then(
+ (res) => res.arrayBuffer(),
+ ),
+ fetch(`https://fonts.cdnfonts.com/s/19795/Inter-Bold.woff`).then((res) =>
+ res.arrayBuffer(),
+ ),
+ ]);
+
+ return [
+ {
+ name: 'Inter',
+ data: interRegular,
+ style: 'normal',
+ weight: 400,
+ },
+ {
+ name: 'Inter',
+ data: interSemiBold,
+ style: 'normal',
+ weight: 600,
+ },
+ {
+ name: 'Inter',
+ data: interBold,
+ style: 'normal',
+ weight: 700,
+ },
+ ];
+}
diff --git a/src/components/Menus/WalletMenu/WalletCard.style.ts b/src/components/Menus/WalletMenu/WalletCard.style.ts
index 7bd599a5f..82a16b214 100644
--- a/src/components/Menus/WalletMenu/WalletCard.style.ts
+++ b/src/components/Menus/WalletMenu/WalletCard.style.ts
@@ -1,13 +1,10 @@
'use client';
-import { avatarMask32 } from '@/components/Mask.style';
+import { ButtonTransparent } from '@/components/Button';
import type { Breakpoint } from '@mui/material';
-import { alpha } from '@mui/material';
-import { Avatar, Badge, Container } from '@mui/material';
-import { styled } from '@mui/material/styles';
+import { alpha, Avatar, Badge, Container } from '@mui/material';
import type { ButtonProps as MuiButtonProps } from '@mui/material/Button/Button';
-import { getContrastAlphaColor } from '@/utils/colors';
-import { ButtonTransparent } from '@/components/Button';
+import { styled } from '@mui/material/styles';
export const WalletAvatar = styled(Avatar)(({ theme }) => ({
margin: 'auto',
diff --git a/src/components/Menus/WalletMenu/WalletMenu.style.ts b/src/components/Menus/WalletMenu/WalletMenu.style.ts
index ebbaf06ca..836ed4946 100644
--- a/src/components/Menus/WalletMenu/WalletMenu.style.ts
+++ b/src/components/Menus/WalletMenu/WalletMenu.style.ts
@@ -3,9 +3,7 @@
import { ButtonSecondary, ButtonTransparent } from '@/components/Button';
import { avatarMask32 } from '@/components/Mask.style';
import type { Breakpoint, ButtonProps } from '@mui/material';
-import { Box, Drawer } from '@mui/material';
-import { alpha } from '@mui/material';
-import { Avatar, Badge, Container } from '@mui/material';
+import { alpha, Avatar, Badge, Container, Drawer } from '@mui/material';
import { styled } from '@mui/material/styles';
export interface WalletButtonProps extends ButtonProps {
diff --git a/src/components/Navbar/WalletButton.style.ts b/src/components/Navbar/WalletButton.style.ts
index 57a069266..483da6d9c 100644
--- a/src/components/Navbar/WalletButton.style.ts
+++ b/src/components/Navbar/WalletButton.style.ts
@@ -1,9 +1,9 @@
import { ButtonPrimary } from '@/components/Button';
import { alpha, Avatar, Badge, Skeleton, styled } from '@mui/material';
+import Image from 'next/image';
import { getContrastAlphaColor } from 'src/utils/colors';
import { ButtonTransparent } from '../Button';
import { avatarMask12 } from '../Mask.style';
-import Image from 'next/image';
export const WalletMgmtWalletAvatar = styled(Avatar)(() => ({
height: 32,
diff --git a/src/components/Navbar/WalletButton.tsx b/src/components/Navbar/WalletButton.tsx
index e56371c2e..71ad5fdb1 100644
--- a/src/components/Navbar/WalletButton.tsx
+++ b/src/components/Navbar/WalletButton.tsx
@@ -3,15 +3,20 @@ import { useChains } from '@/hooks/useChains';
import { useMenuStore } from '@/stores/menu';
import { walletDigest } from '@/utils/walletDigest';
import type { Chain } from '@lifi/sdk';
-import type { Theme } from '@mui/material';
import {
getConnectorIcon,
useAccount,
useWalletMenu,
} from '@lifi/wallet-management';
+import type { Theme } from '@mui/material';
import { Stack, Typography, useMediaQuery } from '@mui/material';
+import { usePathname, useRouter } from 'next/navigation';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
+import { JUMPER_LOYALTY_PATH, JUMPER_SCAN_PATH } from 'src/const/urls';
+import useImageStatus from 'src/hooks/useImageStatus';
+import { useLoyaltyPass } from 'src/hooks/useLoyaltyPass';
+import { XPIcon } from '../illustrations/XPIcon';
import {
ConnectButton,
ImageWalletMenuButton,
@@ -21,12 +26,6 @@ import {
WalletMgmtChainAvatar,
WalletMgmtWalletAvatar,
} from './WalletButton.style';
-import { XPIcon } from '../illustrations/XPIcon';
-import { useLoyaltyPass } from 'src/hooks/useLoyaltyPass';
-import { JUMPER_LOYALTY_PATH, JUMPER_SCAN_PATH } from 'src/const/urls';
-import { usePathname, useRouter } from 'next/navigation';
-import useImageStatus from 'src/hooks/useImageStatus';
-import useEffigyLink from 'src/hooks/useEffigyLink';
export const WalletButtons = () => {
const { chains } = useChains();