Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/orange-snakes-stay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stakekit/widget": patch
---

feat: ledger live usde banner
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"knip": "^5.57.1",
"turbo": "^2.5.3"
},
"packageManager": "pnpm@10.11.0",
"packageManager": "pnpm@10.11.1",
"pnpm": {
"overrides": {
"@types/react": "19.0.10",
Expand Down
17 changes: 17 additions & 0 deletions packages/widget/src/components/atoms/icons/balance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const Balance = () => (
<svg
width="14"
height="15"
viewBox="0 0 14 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.88918 4.62981L1.94493 5.57314L1 4.62981M1.94496 5.51975V4.09577C1.94496 2.79311 3.002 1.73712 4.30594 1.73712M11.1108 10.8432L12.0551 9.89989L13 10.8432M12.055 9.95329V11.3773C12.055 12.6799 10.998 13.7359 9.69403 13.7359M12.77 4.37589C12.77 2.91857 11.5875 1.73725 10.1287 1.73724C8.66996 1.73724 7.4868 2.91856 7.4868 4.37588C7.4868 5.8332 8.66996 7.01452 10.1287 7.01452C11.5875 7.01452 12.77 5.83321 12.77 4.37589ZM1.23065 11.0985C1.23065 12.5558 2.41314 13.7371 3.87191 13.7371C5.33067 13.7371 6.51384 12.5558 6.51384 11.0985C6.51384 9.64116 5.33068 8.45984 3.87191 8.45984C2.41315 8.45984 1.23066 9.64116 1.23065 11.0985Z"
stroke="white"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
6 changes: 5 additions & 1 deletion packages/widget/src/domain/types/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import type { TokenDto } from "@stakekit/api-hooks";
import { EvmNetworks, type TokenDto } from "@stakekit/api-hooks";

export type TokenString = `${TokenDto["network"]}-${TokenDto["address"]}`;

export const isUSDeToken = (token: TokenDto) =>
token.network === EvmNetworks.ethereum &&
token.address === "0x4c9edd5852cd905f086c759e8383e09bff1e68b3";
Comment thread
petar-omni marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { Box, NumberInput, Text } from "@sk-widget/components";
import { ContentLoaderSquare } from "@sk-widget/components/atoms/content-loader";
import { Balance } from "@sk-widget/components/atoms/icons/balance";
import { MaxButton } from "@sk-widget/components/atoms/max-button";
import * as AmountToggle from "@sk-widget/components/molecules/amount-toggle";
import { priceTxt } from "@sk-widget/pages/details/earn-page/components/select-token-section/styles.css";
import { isUSDeToken } from "@sk-widget/domain/types";
import {
bottomBanner,
bottomBannerBottomRadius,
bottomBannerText,
priceTxt,
} from "@sk-widget/pages/details/earn-page/components/select-token-section/styles.css";
import { useEarnPageContext } from "@sk-widget/pages/details/earn-page/state/earn-page-context";
import { useSettings } from "@sk-widget/providers/settings";
import { Just } from "purify-ts";
import { useSKWallet } from "@sk-widget/providers/sk-wallet";
import clsx from "clsx";
import { Just, Maybe } from "purify-ts";
import { useTranslation } from "react-i18next";
import { SelectToken } from "./select-token";
import { SelectTokenTitle } from "./title";
Expand All @@ -15,6 +24,8 @@ export const SelectTokenSection = () => {

const { variant } = useSettings();

const { isLedgerLive } = useSKWallet();

const {
appLoading,
selectedTokenAvailableAmount,
Expand All @@ -28,6 +39,7 @@ export const SelectTokenSection = () => {
stakeMinAmount,
symbol,
isStakeTokenSameAsGasToken,
selectedToken,
} = useEarnPageContext();

const isLoading = appLoading || selectTokenIsLoading;
Expand All @@ -50,6 +62,18 @@ export const SelectTokenSection = () => {

const errorBalance = stakeAmountGreaterThanAvailableAmount;

const showBottomUSDeBanner = Maybe.fromRecord({
selectedTokenAvailableAmount,
selectedToken,
})
.filter(
(val) =>
isLedgerLive &&
val.selectedTokenAvailableAmount.amount.isZero() &&
isUSDeToken(val.selectedToken)
)
.isJust();

const minStakeAmount = Just([
stakeMinAmount
.map((v) => `${t("shared.min")} ${v} ${symbol}`)
Expand Down Expand Up @@ -82,110 +106,133 @@ export const SelectTokenSection = () => {
<ContentLoaderSquare heightPx={112.5} />
</Box>
) : (
<Box
data-rk="stake-token-section"
background="stakeSectionBackground"
borderRadius="xl"
marginTop="2"
py="4"
px="4"
borderStyle="solid"
borderWidth={1}
borderColor={
submitted && stakeAmountIsZero ? "textDanger" : "transparent"
}
>
{variant === "zerion" && (
<Box display="flex" justifyContent="space-between">
<SelectTokenTitle />
{minStakeAmount}
</Box>
)}

<Box display="flex" justifyContent="space-between" alignItems="center">
<Box minWidth="0" display="flex" flex={1}>
<NumberInput
shakeOnInvalid
isInvalid={errorInput}
onChange={onStakeAmountChange}
value={stakeAmount}
/>
</Box>

<Box display="flex" justifyContent="center" alignItems="center">
<SelectToken />
</Box>
</Box>

{variant !== "zerion" && minStakeAmount}

<Box>
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
data-rk="stake-token-section"
background="stakeSectionBackground"
borderRadius="xl"
marginTop="2"
flexWrap="wrap"
data-rk="stake-token-section-balance"
gap="1"
py="4"
px="4"
borderStyle="solid"
borderWidth={1}
borderColor={
submitted && stakeAmountIsZero ? "textDanger" : "transparent"
}
className={clsx({
[bottomBannerBottomRadius]: showBottomUSDeBanner,
})}
>
<Box className={priceTxt} display="flex">
<Text variant={{ type: "muted", weight: "normal" }}>
{formattedPrice}
</Text>
{variant === "zerion" && (
<Box display="flex" justifyContent="space-between">
<SelectTokenTitle />
{minStakeAmount}
</Box>
)}

<Box display="flex" justifyContent="space-between" alignItems="center">
<Box minWidth="0" display="flex" flex={1}>
<NumberInput
shakeOnInvalid
isInvalid={errorInput}
onChange={onStakeAmountChange}
value={stakeAmount}
/>
</Box>

<Box display="flex" justifyContent="center" alignItems="center">
<SelectToken />
</Box>
</Box>

{variant !== "zerion" && minStakeAmount}

<Box
flexGrow={1}
display="flex"
justifyContent="space-between"
alignItems="center"
marginTop="2"
flexWrap="wrap"
data-rk="stake-token-section-balance"
gap="1"
>
<Box display="flex">
<Text
variant={{
weight: "normal",
type: errorBalance ? "danger" : "muted",
}}
data-state={errorBalance ? "error" : "valid"}
>
{selectedTokenAvailableAmount
.map((v) =>
variant === "zerion" ? (
<>
<span>{t("shared.balance")}:&nbsp;</span>
<Box
{...(isStakeTokenSameAsGasToken
? { as: "span" }
: {
onClick: onMaxClick,
as: "button",
})}
>
{v.shortFormattedAmount}&nbsp;{v.symbol}
</Box>
</>
) : (
<AmountToggle.Root>
<AmountToggle.Amount>
{({ state }) => (
<span>
{state === "full"
? v.fullFormattedAmount
: v.shortFormattedAmount}
&nbsp;{v.symbol}&nbsp;{t("shared.available")}
</span>
)}
</AmountToggle.Amount>
</AmountToggle.Root>
)
)
.extractNullable()}
<Box className={priceTxt} display="flex">
<Text variant={{ type: "muted", weight: "normal" }}>
{formattedPrice}
</Text>
</Box>

{!isStakeTokenSameAsGasToken && <MaxButton onMaxClick={onMaxClick} />}
<Box
flexGrow={1}
display="flex"
justifyContent="space-between"
alignItems="center"
>
<Box display="flex">
<Text
variant={{
weight: "normal",
type: errorBalance ? "danger" : "muted",
}}
data-state={errorBalance ? "error" : "valid"}
>
{selectedTokenAvailableAmount
.map((v) =>
variant === "zerion" ? (
<>
<span>{t("shared.balance")}:&nbsp;</span>
<Box
{...(isStakeTokenSameAsGasToken
? { as: "span" }
: {
onClick: onMaxClick,
as: "button",
})}
>
{v.shortFormattedAmount}&nbsp;{v.symbol}
</Box>
</>
) : (
<AmountToggle.Root>
<AmountToggle.Amount>
{({ state }) => (
<span>
{state === "full"
? v.fullFormattedAmount
: v.shortFormattedAmount}
&nbsp;{v.symbol}&nbsp;{t("shared.available")}
</span>
)}
</AmountToggle.Amount>
</AmountToggle.Root>
)
)
.extractNullable()}
</Text>
</Box>

{!isStakeTokenSameAsGasToken && (
<MaxButton onMaxClick={onMaxClick} />
)}
</Box>
</Box>
</Box>

{showBottomUSDeBanner && (
<Box
className={bottomBanner}
px="2"
py="3"
display="flex"
gap="2"
justifyContent="center"
>
<Balance />
<Text className={bottomBannerText}>
{t("select_token.usde_banner")}
</Text>
</Box>
)}
</Box>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
import { atoms } from "@sk-widget/styles/theme";
import { style } from "@vanilla-extract/css";

export const priceTxt = style({
flexGrow: 999,
});

export const bottomBannerBottomRadius = style({
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
});

export const bottomBanner = style([
atoms({ borderRadius: "xl" }),
{
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
background:
"linear-gradient(90deg, #4B2921 0%, #723426 25.78%, #994238 59.59%, #7C3C48 100%)",
},
]);

export const bottomBannerText = style({
fontSize: "12px",
});
3 changes: 2 additions & 1 deletion packages/widget/src/translation/English/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,8 @@
},
"select_token": {
"title": "Select token",
"n_available_opps": "{{count}} available opportunities"
"n_available_opps": "{{count}} available opportunities",
"usde_banner": "Missing USDe balance? Swap now and start earning!"
},
"referral_lock": {
"title": "Referral check",
Expand Down