diff --git a/.changeset/angry-beans-yawn.md b/.changeset/angry-beans-yawn.md
new file mode 100644
index 00000000..8671abf6
--- /dev/null
+++ b/.changeset/angry-beans-yawn.md
@@ -0,0 +1,5 @@
+---
+"@stakekit/widget": patch
+---
+
+feat: tokens for enabled yields only option
diff --git a/.changeset/clean-teeth-love.md b/.changeset/clean-teeth-love.md
new file mode 100644
index 00000000..a5ad9fdf
--- /dev/null
+++ b/.changeset/clean-teeth-love.md
@@ -0,0 +1,5 @@
+---
+"@stakekit/widget": patch
+---
+
+feat(externalProviders): add tron support
diff --git a/.changeset/six-rats-return.md b/.changeset/six-rats-return.md
new file mode 100644
index 00000000..52fe1a19
--- /dev/null
+++ b/.changeset/six-rats-return.md
@@ -0,0 +1,5 @@
+---
+"@stakekit/widget": patch
+---
+
+feat(externalProviders): display signTransaction message
diff --git a/.changeset/sour-papers-grow.md b/.changeset/sour-papers-grow.md
new file mode 100644
index 00000000..ef326a2b
--- /dev/null
+++ b/.changeset/sour-papers-grow.md
@@ -0,0 +1,5 @@
+---
+"@stakekit/widget": patch
+---
+
+feat(externalProviders): provider details in txMeta
diff --git a/.changeset/stupid-lights-taste.md b/.changeset/stupid-lights-taste.md
new file mode 100644
index 00000000..488f5b9f
--- /dev/null
+++ b/.changeset/stupid-lights-taste.md
@@ -0,0 +1,5 @@
+---
+"@stakekit/widget": patch
+---
+
+fix(bundledWidget): allow multiple re-renders
diff --git a/.changeset/three-garlics-remain.md b/.changeset/three-garlics-remain.md
new file mode 100644
index 00000000..fbd1f4a8
--- /dev/null
+++ b/.changeset/three-garlics-remain.md
@@ -0,0 +1,5 @@
+---
+"@stakekit/widget": patch
+---
+
+feat(tracking): add action steps cancelled event
diff --git a/packages/widget/package.json b/packages/widget/package.json
index 10b07041..0691df06 100644
--- a/packages/widget/package.json
+++ b/packages/widget/package.json
@@ -71,7 +71,7 @@
"@radix-ui/react-visually-hidden": "^1.1.3",
"@safe-global/safe-apps-provider": "^0.18.6",
"@safe-global/safe-apps-sdk": "^9.1.0",
- "@stakekit/api-hooks": "0.0.98",
+ "@stakekit/api-hooks": "0.0.100",
"@stakekit/common": "^0.0.48",
"@stakekit/rainbowkit": "^2.2.4",
"@tanstack/react-query": "^5.74.0",
diff --git a/packages/widget/src/App.tsx b/packages/widget/src/App.tsx
index 90649869..a29c2625 100644
--- a/packages/widget/src/App.tsx
+++ b/packages/widget/src/App.tsx
@@ -239,14 +239,14 @@ export const SKApp = (props: SKAppProps) => {
};
export type BundledSKWidgetProps = SKAppProps & {
- ref?: RefObject<{ rerender: (newProps: SKAppProps) => void }>;
+ ref?: RefObject<{ rerender: (newProps: BundledSKWidgetProps) => void }>;
};
const BundledSKWidget = (_props: BundledSKWidgetProps) => {
const [props, setProps] = useState(_props);
useImperativeHandle(props.ref, () => ({
- rerender: (newProps: SKAppProps) => setProps(newProps),
+ rerender: (newProps: BundledSKWidgetProps) => setProps(newProps),
}));
return ;
@@ -269,6 +269,7 @@ export const renderSKWidget = ({
root.render();
return {
- rerender: (newProps: SKAppProps) => appRef.current.rerender(newProps),
+ rerender: (newProps: SKAppProps) =>
+ appRef.current.rerender({ ...newProps, ref: appRef }),
};
};
diff --git a/packages/widget/src/common/get-token-balances.ts b/packages/widget/src/common/get-token-balances.ts
index 62a49125..a5ea8220 100644
--- a/packages/widget/src/common/get-token-balances.ts
+++ b/packages/widget/src/common/get-token-balances.ts
@@ -3,21 +3,28 @@ import { EitherAsync, Right } from "purify-ts";
import type { SKWallet } from "../domain/types";
import { getDefaultTokens } from "../hooks/api/use-default-tokens";
import { getTokenBalancesScan } from "../hooks/api/use-token-balances-scan";
+import type { SettingsProps } from "../providers/settings";
export const getTokenBalances = ({
additionalAddresses,
address,
network,
queryClient,
+ tokensForEnabledYieldsOnly,
}: {
additionalAddresses: SKWallet["additionalAddresses"];
address: SKWallet["address"];
queryClient: QueryClient;
network: SKWallet["network"];
+ tokensForEnabledYieldsOnly: SettingsProps["tokensForEnabledYieldsOnly"];
}) =>
EitherAsync.fromPromise(() =>
Promise.all([
- getDefaultTokens({ queryClient, network }),
+ getDefaultTokens({
+ queryClient,
+ network: network ?? undefined,
+ enabledYieldsOnly: tokensForEnabledYieldsOnly,
+ }),
EitherAsync.liftEither(
Right({ additionalAddresses, address, network })
).chain(async (params) => {
diff --git a/packages/widget/src/domain/types/chains.ts b/packages/widget/src/domain/types/chains.ts
index da7e8e6f..462fba1a 100644
--- a/packages/widget/src/domain/types/chains.ts
+++ b/packages/widget/src/domain/types/chains.ts
@@ -124,6 +124,10 @@ export const isTonChain = (chain: string): chain is SupportedMiscChains => {
return chain === MiscNetworks.Ton;
};
+export const isTronChain = (chain: string): chain is SupportedMiscChains => {
+ return chain === MiscNetworks.Tron;
+};
+
export const isSupportedChain = (chain: string): chain is SupportedSKChains => {
return (
isEvmChain(chain) ||
diff --git a/packages/widget/src/domain/types/external-providers.ts b/packages/widget/src/domain/types/external-providers.ts
index 197fc728..6b9629a0 100644
--- a/packages/widget/src/domain/types/external-providers.ts
+++ b/packages/widget/src/domain/types/external-providers.ts
@@ -1,36 +1,33 @@
-import type { ActionDto, TransactionDto } from "@stakekit/api-hooks";
-import { EitherAsync, Left } from "purify-ts";
+import { EitherAsync, Left, Maybe, Right } from "purify-ts";
import type { RefObject } from "react";
import type { SKExternalProviders } from "./wallets";
-import type { SKTx } from "./wallets/generic-wallet";
+import type { SKTx, SKTxMeta } from "./wallets/generic-wallet";
export class ExternalProvider {
constructor(private variantProvider: RefObject) {}
- private invalidProviderType() {
- return EitherAsync.liftEither(Left(new Error("Invalid provider type")));
- }
-
- sendTransaction(
- tx: SKTx,
- txMeta: {
- txId: TransactionDto["id"];
- actionId: ActionDto["id"];
- actionType: ActionDto["type"];
- txType: TransactionDto["type"];
- }
- ) {
- const _sendTransaction =
- this.variantProvider.current.provider.sendTransaction;
+ sendTransaction(tx: SKTx, txMeta: SKTxMeta) {
+ return EitherAsync.liftEither(
+ Maybe.fromNullable(
+ this.variantProvider.current.provider.sendTransaction
+ ).toEither(new Error("Invalid provider type"))
+ )
+ .chain((_sendTransaction) =>
+ EitherAsync(() => _sendTransaction(tx, txMeta)).mapLeft(
+ () => new Error("Failed to send transaction, unknown error")
+ )
+ )
+ .chain((res) => {
+ if (typeof res === "string") {
+ return EitherAsync.liftEither(Right(res));
+ }
- if (!_sendTransaction) {
- return this.invalidProviderType();
- }
+ if (res.type === "success") {
+ return EitherAsync.liftEither(Right(res.txHash));
+ }
- return EitherAsync(() => _sendTransaction(tx, txMeta)).mapLeft((e) => {
- console.log(e);
- return new Error("Failed to send transaction");
- });
+ return EitherAsync.liftEither(Left(res.error));
+ });
}
switchChain({ chainId }: { chainId: number }) {
diff --git a/packages/widget/src/domain/types/wallet.ts b/packages/widget/src/domain/types/wallet.ts
index 0f884fc0..6d390fa3 100644
--- a/packages/widget/src/domain/types/wallet.ts
+++ b/packages/widget/src/domain/types/wallet.ts
@@ -1,6 +1,7 @@
import type { Account } from "@ledgerhq/wallet-api-client";
+import type { SendTransactionError } from "@sk-widget/providers/sk-wallet/errors";
+import type { TransactionDecodeError } from "@sk-widget/providers/sk-wallet/errors";
import type {
- ActionDto,
AddressWithTokenDtoAdditionalAddresses,
Networks,
TransactionDto,
@@ -8,12 +9,9 @@ import type {
import type { EitherAsync } from "purify-ts";
import type { Chain } from "viem";
import type { Connector } from "wagmi";
-import type {
- SendTransactionError,
- TransactionDecodeError,
-} from "../../pages/steps/hooks/errors";
import type { Nullable } from "../../types";
import type { SupportedSKChains } from "./chains";
+import type { SKTxMeta } from "./wallets/generic-wallet";
type SignedTxOrMessage = string;
@@ -21,12 +19,7 @@ export type SKWallet = {
disconnect: () => Promise;
signTransaction: (args: {
tx: NonNullable;
- txMeta: {
- txId: TransactionDto["id"];
- actionId: ActionDto["id"];
- actionType: ActionDto["type"];
- txType: TransactionDto["type"];
- };
+ txMeta: SKTxMeta;
ledgerHwAppId: Nullable;
network: Networks;
}) => EitherAsync<
diff --git a/packages/widget/src/domain/types/wallets/generic-wallet.ts b/packages/widget/src/domain/types/wallets/generic-wallet.ts
index 8855f7cf..a60496a1 100644
--- a/packages/widget/src/domain/types/wallets/generic-wallet.ts
+++ b/packages/widget/src/domain/types/wallets/generic-wallet.ts
@@ -1,8 +1,11 @@
-import type { ActionDto, TransactionDto } from "@stakekit/api-hooks";
+import type {
+ ActionDto,
+ RewardTypes,
+ TransactionDto,
+} from "@stakekit/api-hooks";
+import type * as TronWeb from "tronweb";
import type { Hex } from "viem";
-type Base64String = string;
-
export enum TxType {
Legacy = "0x1",
EIP1559 = "0x2",
@@ -29,17 +32,42 @@ export type EVMTx = {
);
};
-export type SolanaTx = { type: "solana"; tx: Base64String };
+export type SolanaTx = { type: "solana"; tx: string };
export type TonTx = {
type: "ton";
tx: {
seqno: bigint;
- message: Base64String;
+ message: string;
};
};
-export type SKTx = EVMTx | SolanaTx | TonTx;
+export type TronTx = {
+ type: "tron";
+ tx: TronWeb.Types.Transaction;
+};
+
+export type SKTx = EVMTx | SolanaTx | TonTx | TronTx;
+
+export type ActionMeta = {
+ actionId: ActionDto["id"];
+ actionType: ActionDto["type"];
+ amount: ActionDto["amount"];
+ inputToken: ActionDto["inputToken"];
+ providersDetails: {
+ name: string;
+ address: string | undefined;
+ rewardRate: number | undefined;
+ rewardType: RewardTypes;
+ website: string | undefined;
+ logo: string | undefined;
+ }[];
+};
+
+export type SKTxMeta = ActionMeta & {
+ txId: TransactionDto["id"];
+ txType: TransactionDto["type"];
+};
export type SKWallet = {
signMessage: (message: string) => Promise;
@@ -47,11 +75,10 @@ export type SKWallet = {
getTransactionReceipt?(txHash: string): Promise<{ transactionHash?: string }>;
sendTransaction(
tx: SKTx,
- txMeta: {
- txId: TransactionDto["id"];
- actionId: ActionDto["id"];
- actionType: ActionDto["type"];
- txType: TransactionDto["type"];
- }
- ): Promise;
+ txMeta: SKTxMeta
+ ): Promise<
+ | string
+ | { type: "success"; txHash: string }
+ | { type: "error"; error: string }
+ >;
};
diff --git a/packages/widget/src/hooks/api/use-default-tokens.ts b/packages/widget/src/hooks/api/use-default-tokens.ts
index 5ea65e73..f07404d4 100644
--- a/packages/widget/src/hooks/api/use-default-tokens.ts
+++ b/packages/widget/src/hooks/api/use-default-tokens.ts
@@ -1,17 +1,27 @@
-import type { TokenBalanceScanResponseDto } from "@stakekit/api-hooks";
+import { useSettings } from "@sk-widget/providers/settings";
+import type {
+ TokenBalanceScanResponseDto,
+ TokenGetTokensParams,
+} from "@stakekit/api-hooks";
import { getTokenGetTokensQueryKey, tokenGetTokens } from "@stakekit/api-hooks";
import type { QueryClient } from "@tanstack/react-query";
import { useQuery } from "@tanstack/react-query";
import { EitherAsync } from "purify-ts";
-import type { SKWallet } from "../../domain/types";
import { useSKWallet } from "../../providers/sk-wallet";
export const useDefaultTokens = () => {
const { network } = useSKWallet();
+ const { tokensForEnabledYieldsOnly } = useSettings();
return useQuery({
queryKey: getTokenGetTokensQueryKey({ network: network ?? undefined }),
- queryFn: async () => (await queryFn({ network })).unsafeCoerce(),
+ queryFn: async () =>
+ (
+ await queryFn({
+ network: network ?? undefined,
+ enabledYieldsOnly: !!tokensForEnabledYieldsOnly,
+ })
+ ).unsafeCoerce(),
staleTime: 1000 * 60 * 5,
});
};
@@ -33,10 +43,13 @@ export const getDefaultTokens = (
const queryFn = ({
network,
-}: {
- network: SKWallet["network"];
-}) =>
- EitherAsync(() => tokenGetTokens({ network: network ?? undefined })).map(
- (val) =>
- val.map((v) => ({ ...v, amount: "0" }))
+ enabledYieldsOnly,
+}: Pick) =>
+ EitherAsync(() =>
+ tokenGetTokens({
+ network,
+ enabledYieldsOnly: enabledYieldsOnly || undefined,
+ })
+ ).map((val) =>
+ val.map((v) => ({ ...v, amount: "0" }))
);
diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts
index 9313d355..1a21dff8 100644
--- a/packages/widget/src/pages/details/earn-page/state/use-init-token.ts
+++ b/packages/widget/src/pages/details/earn-page/state/use-init-token.ts
@@ -23,7 +23,7 @@ export const useInitToken = () => {
const queryClient = useSKQueryClient();
const { data: positionsData } = usePositionsData();
- const { externalProviders } = useSettings();
+ const { externalProviders, tokensForEnabledYieldsOnly } = useSettings();
return useQuery({
staleTime: Number.POSITIVE_INFINITY,
@@ -41,6 +41,7 @@ export const useInitToken = () => {
address,
network,
queryClient,
+ tokensForEnabledYieldsOnly,
}).chain((val) =>
getInitParams({
isLedgerLive,
diff --git a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts
index eba02730..f02e0044 100644
--- a/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts
+++ b/packages/widget/src/pages/details/earn-page/state/use-init-yield.ts
@@ -21,7 +21,7 @@ export const useInitYield = ({
const { isLedgerLive, isConnected, network, additionalAddresses, address } =
useSKWallet();
const queryClient = useSKQueryClient();
- const { externalProviders } = useSettings();
+ const { externalProviders, tokensForEnabledYieldsOnly } = useSettings();
const { data: positionsData } = usePositionsData();
return useQuery({
@@ -44,6 +44,7 @@ export const useInitYield = ({
address,
network,
queryClient,
+ tokensForEnabledYieldsOnly,
})
.chain((val) =>
EitherAsync.liftEither(
diff --git a/packages/widget/src/pages/steps/hooks/errors.ts b/packages/widget/src/pages/steps/hooks/errors.ts
index 4f80c7c9..221529ec 100644
--- a/packages/widget/src/pages/steps/hooks/errors.ts
+++ b/packages/widget/src/pages/steps/hooks/errors.ts
@@ -1,16 +1,12 @@
-export class SendTransactionError extends Error {
- name = "SendTransactionError";
-}
-export class NotSupportedFlowError extends Error {
- name = "NotSupportedFlowError";
-}
export class SignError extends Error {
+ _tag = "SignError";
txId: string;
network: string;
- name = "SignError";
constructor({ network, txId }: { txId: string; network: string }) {
super();
+
+ this._tag = "SignError";
this.txId = txId;
this.network = network;
}
@@ -19,14 +15,16 @@ export class GetStakeSessionError extends Error {
name = "GetStakeSessionError";
}
export class TransactionConstructError extends Error {
- name = "TransactionConstructError";
+ _tag = "TransactionConstructError";
+
+ constructor(message?: string) {
+ super(message);
+ this._tag = "TransactionConstructError";
+ }
}
export class TXCheckError extends Error {
name = "TXCheckError";
}
-export class TransactionDecodeError extends Error {
- name = "TransactionDecodeError";
-}
export class SubmitHashError extends Error {
name = "SubmitHashError";
}
diff --git a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts
index d61ddce7..fdd30135 100644
--- a/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts
+++ b/packages/widget/src/pages/steps/hooks/use-steps-machine.hook.ts
@@ -1,4 +1,9 @@
+import type { ActionMeta } from "@sk-widget/domain/types/wallets/generic-wallet";
import { useSavedRef } from "@sk-widget/hooks";
+import type {
+ SendTransactionError,
+ TransactionDecodeError,
+} from "@sk-widget/providers/sk-wallet/errors";
import type { ActionDto, TransactionDto } from "@stakekit/api-hooks";
import {
transactionConstruct,
@@ -17,7 +22,7 @@ import { withRequestErrorRetry } from "../../../common/utils";
import { isTxError } from "../../../domain";
import { useTrackEvent } from "../../../hooks/tracking/use-track-event";
import { useSKWallet } from "../../../providers/sk-wallet";
-import type { GetStakeSessionError, SendTransactionError } from "./errors";
+import type { GetStakeSessionError } from "./errors";
import {
SignError,
SubmitError,
@@ -31,10 +36,9 @@ type TxMeta = {
signedTx: string | null;
broadcasted: boolean | null;
signError:
- | Error
- | GetStakeSessionError
| SendTransactionError
- | SignError
+ | TransactionDecodeError
+ | TransactionConstructError
| null;
txCheckError: GetStakeSessionError | null;
done: boolean;
@@ -58,13 +62,11 @@ type SignRes =
export const useStepsMachine = ({
transactions,
integrationId,
- actionId,
- actionType,
+ actionMeta,
}: {
transactions: ActionDto["transactions"];
integrationId: ActionDto["integrationId"];
- actionId: ActionDto["id"];
- actionType: ActionDto["type"];
+ actionMeta: ActionMeta;
}) => {
const { signTransaction, signMessage, isLedgerLive } = useSKWallet();
@@ -82,8 +84,7 @@ export const useStepsMachine = ({
trackEvent,
signMessage,
signTransaction,
- actionId,
- actionType,
+ actionMeta,
});
return useMachine(useState(() => getMachine(machineParams))[0]);
@@ -98,8 +99,7 @@ const getMachine = (
trackEvent: ReturnType;
signMessage: ReturnType["signMessage"];
signTransaction: ReturnType["signTransaction"];
- actionId: ActionDto["id"];
- actionType: ActionDto["type"];
+ actionMeta: ActionMeta;
}>
>
) => {
@@ -130,7 +130,14 @@ const getMachine = (
type: "__SIGN_SUCCESS__";
val: Extract;
}
- | { type: "__SIGN_ERROR__"; val: Error }
+ | {
+ type: "__SIGN_ERROR__";
+ val:
+ | SendTransactionError
+ | TransactionDecodeError
+ | TransactionConstructError
+ | SignError;
+ }
| { type: "__BROADCAST_SUCCESS__" }
| { type: "__BROADCAST_ERROR__"; val: Error | SubmitHashError }
| { type: "__DONE__"; val: TxState[] }
@@ -223,15 +230,16 @@ const getMachine = (
EitherAsync.liftEither(
context.currentTxMeta
.chainNullable((v) => context.txStates[v.idx].tx)
- .toEither(new Error("missing tx"))
+ .toEither(new TransactionConstructError("missing tx"))
)
- .chain((tx) => {
- /**
- * Single sign transactions
- */
- return getAverageGasMode({
- network: tx.network,
- })
+ .chain<
+ | TransactionConstructError
+ | SendTransactionError
+ | TransactionDecodeError
+ | SignError,
+ SignRes
+ >((tx) =>
+ getAverageGasMode({ network: tx.network })
.chainLeft(async () => Right(null))
.chain((gas) =>
txConstruct(tx.id, {
@@ -239,7 +247,13 @@ const getMachine = (
ledgerWalletAPICompatible: ref.current.isLedgerLive,
}).mapLeft(() => new TransactionConstructError())
)
- .chain((constructedTx) => {
+ .chain<
+ | TransactionConstructError
+ | SendTransactionError
+ | TransactionDecodeError
+ | SignError,
+ SignRes
+ >((constructedTx) => {
if (
constructedTx.status === "BROADCASTED" ||
constructedTx.status === "CONFIRMED"
@@ -259,9 +273,16 @@ const getMachine = (
return ref.current
.signMessage(constructedTx.unsignedTransaction)
.map((val) => ({
- type: "regular",
+ type: "regular" as const,
data: { signedTx: val, broadcasted: false },
- }));
+ }))
+ .mapLeft(
+ () =>
+ new SignError({
+ network: constructedTx.network,
+ txId: constructedTx.id,
+ })
+ );
}
return ref.current
@@ -269,9 +290,8 @@ const getMachine = (
tx: constructedTx.unsignedTransaction,
ledgerHwAppId: constructedTx.ledgerHwAppId,
txMeta: {
- actionId: ref.current.actionId,
+ ...ref.current.actionMeta,
txId: constructedTx.id,
- actionType: ref.current.actionType,
txType: constructedTx.type,
},
network: constructedTx.network,
@@ -289,8 +309,8 @@ const getMachine = (
})
)
.map((val) => ({ type: "regular", data: val }));
- });
- })
+ })
+ )
.caseOf({
Left: (l) => {
console.log(l);
diff --git a/packages/widget/src/pages/steps/hooks/use-steps.hook.ts b/packages/widget/src/pages/steps/hooks/use-steps.hook.ts
index ed8dfec5..62ac90d7 100644
--- a/packages/widget/src/pages/steps/hooks/use-steps.hook.ts
+++ b/packages/widget/src/pages/steps/hooks/use-steps.hook.ts
@@ -1,3 +1,6 @@
+import type { ActionMeta } from "@sk-widget/domain/types/wallets/generic-wallet";
+import { useTrackEvent } from "@sk-widget/hooks/tracking/use-track-event";
+import type { useProvidersDetails } from "@sk-widget/hooks/use-provider-details";
import { useSetActionHistoryData } from "@sk-widget/providers/stake-history";
import type { ActionDto, TransactionType } from "@stakekit/api-hooks";
import { useEffect, useLayoutEffect, useMemo } from "react";
@@ -13,19 +16,42 @@ import { useStepsMachine } from "./use-steps-machine.hook";
export const useSteps = ({
session,
onSignSuccess,
+ providersDetails,
}: {
onSignSuccess?: () => void;
session: ActionDto;
+ providersDetails: ReturnType;
}) => {
const navigate = useNavigate();
const callbacksRef = useSavedRef({ onSignSuccess });
+ const actionMeta = useMemo(
+ (): ActionMeta => ({
+ actionId: session.id,
+ actionType: session.type,
+ amount: session.amount,
+ inputToken: session.inputToken,
+ providersDetails: providersDetails
+ .map((providerDetail) =>
+ providerDetail.map((v) => ({
+ name: v.name,
+ address: v.address,
+ rewardRate: v.rewardRate,
+ rewardType: v.rewardType,
+ website: v.website,
+ logo: v.logo,
+ }))
+ )
+ .orDefault([]),
+ }),
+ [session, providersDetails]
+ );
+
const [machineState, send, actorRef] = useStepsMachine({
transactions: session.transactions,
integrationId: session.integrationId,
- actionId: session.id,
- actionType: session.type,
+ actionMeta,
});
/**
@@ -95,7 +121,12 @@ export const useSteps = ({
machineState.status,
]);
- const onClick = () => navigate(-1);
+ const trackEvent = useTrackEvent();
+
+ const onClick = () => {
+ trackEvent("actionStepsCancelled");
+ navigate(-1);
+ };
const retry = (() => {
if (machineState.matches("signError")) {
diff --git a/packages/widget/src/pages/steps/pages/activity-steps.page.tsx b/packages/widget/src/pages/steps/pages/activity-steps.page.tsx
index 9c624950..581e7126 100644
--- a/packages/widget/src/pages/steps/pages/activity-steps.page.tsx
+++ b/packages/widget/src/pages/steps/pages/activity-steps.page.tsx
@@ -1,15 +1,35 @@
+import { useProvidersDetails } from "@sk-widget/hooks/use-provider-details";
import { useActivityContext } from "@sk-widget/providers/activity-provider";
import { useSelector } from "@xstate/store/react";
+import { Maybe } from "purify-ts";
+import { useMemo } from "react";
import { useTrackPage } from "../../../hooks/tracking/use-track-page";
import { StepsPage } from "./common.page";
export const ActivityStepsPage = () => {
useTrackPage("activitySteps");
+ const activityContext = useActivityContext();
+
const selectedAction = useSelector(
- useActivityContext(),
+ activityContext,
(state) => state.context.selectedAction
).unsafeCoerce();
- return ;
+ const selectedYield = useSelector(
+ activityContext,
+ (state) => state.context.selectedYield
+ ).unsafeCoerce();
+
+ const providersDetails = useProvidersDetails({
+ integrationData: useMemo(() => Maybe.of(selectedYield), [selectedYield]),
+ validatorsAddresses: useMemo(
+ () => Maybe.of(selectedAction.validatorAddresses ?? []),
+ [selectedAction.validatorAddresses]
+ ),
+ });
+
+ return (
+
+ );
};
diff --git a/packages/widget/src/pages/steps/pages/common.page.tsx b/packages/widget/src/pages/steps/pages/common.page.tsx
index e6950458..04fbd526 100644
--- a/packages/widget/src/pages/steps/pages/common.page.tsx
+++ b/packages/widget/src/pages/steps/pages/common.page.tsx
@@ -1,3 +1,4 @@
+import type { useProvidersDetails } from "@sk-widget/hooks/use-provider-details";
import type { ActionDto } from "@stakekit/api-hooks";
import { motion } from "motion/react";
import { useTranslation } from "react-i18next";
@@ -10,12 +11,18 @@ import { TxState } from "./tx-state";
type StepsPageProps = {
session: ActionDto;
onSignSuccess?: () => void;
+ providersDetails: ReturnType;
};
-export const StepsPage = ({ session, onSignSuccess }: StepsPageProps) => {
+export const StepsPage = ({
+ session,
+ onSignSuccess,
+ providersDetails,
+}: StepsPageProps) => {
const { retry, txStates } = useSteps({
session,
onSignSuccess,
+ providersDetails,
});
const { t } = useTranslation();
diff --git a/packages/widget/src/pages/steps/pages/pending-steps.page.tsx b/packages/widget/src/pages/steps/pages/pending-steps.page.tsx
index 7383dc17..c0d4b660 100644
--- a/packages/widget/src/pages/steps/pages/pending-steps.page.tsx
+++ b/packages/widget/src/pages/steps/pages/pending-steps.page.tsx
@@ -1,15 +1,42 @@
+import { useUnstakeOrPendingActionParams } from "@sk-widget/hooks/navigation/use-unstake-or-pending-action-params";
+import { usePositionBalances } from "@sk-widget/hooks/use-position-balances";
+import { useProvidersDetails } from "@sk-widget/hooks/use-provider-details";
import { usePendingActionStore } from "@sk-widget/providers/pending-action-store";
import { useSelector } from "@xstate/store/react";
+import { Maybe } from "purify-ts";
+import { useMemo } from "react";
import { useTrackPage } from "../../../hooks/tracking/use-track-page";
import { StepsPage } from "./common.page";
export const PendingStepsPage = () => {
+ useTrackPage("pendingActionSteps");
+
const pendingRequest = useSelector(
usePendingActionStore(),
(state) => state.context.data
).unsafeCoerce();
- useTrackPage("pendingActionSteps");
+ const { plain } = useUnstakeOrPendingActionParams();
+
+ const positionBalances = usePositionBalances({
+ balanceId: plain.balanceId,
+ integrationId: plain.integrationId,
+ });
+
+ const providersDetails = useProvidersDetails({
+ integrationData: useMemo(
+ () => Maybe.of(pendingRequest.integrationData),
+ [pendingRequest.integrationData]
+ ),
+ validatorsAddresses: positionBalances.data.map((p) =>
+ p.type === "validators" ? p.validatorsAddresses : []
+ ),
+ });
- return ;
+ return (
+
+ );
};
diff --git a/packages/widget/src/pages/steps/pages/stake-steps.page.tsx b/packages/widget/src/pages/steps/pages/stake-steps.page.tsx
index e037e0fa..e170726c 100644
--- a/packages/widget/src/pages/steps/pages/stake-steps.page.tsx
+++ b/packages/widget/src/pages/steps/pages/stake-steps.page.tsx
@@ -1,6 +1,8 @@
+import { useProvidersDetails } from "@sk-widget/hooks/use-provider-details";
import { useEnterStakeStore } from "@sk-widget/providers/enter-stake-store";
import { useSelector } from "@xstate/store/react";
import { Maybe } from "purify-ts";
+import { useMemo } from "react";
import { importValidator } from "../../../common/import-validator";
import { useTrackPage } from "../../../hooks/tracking/use-track-page";
import { useSKWallet } from "../../../providers/sk-wallet";
@@ -33,10 +35,22 @@ export const StakeStepsPage = () => {
)
);
+ const providersDetails = useProvidersDetails({
+ integrationData: useMemo(
+ () => Maybe.of(enterRequest.selectedStake),
+ [enterRequest.selectedStake]
+ ),
+ validatorsAddresses: useMemo(
+ () => Maybe.of(enterRequest.selectedValidators),
+ [enterRequest.selectedValidators]
+ ),
+ });
+
return (
);
};
diff --git a/packages/widget/src/pages/steps/pages/tx-state.tsx b/packages/widget/src/pages/steps/pages/tx-state.tsx
index d67ed191..a2119a16 100644
--- a/packages/widget/src/pages/steps/pages/tx-state.tsx
+++ b/packages/widget/src/pages/steps/pages/tx-state.tsx
@@ -119,7 +119,7 @@ export const TxState = ({ txState, position, count }: Props) => {
{t("steps.approve")}
{txState.state === TxStateEnum.SIGN_ERROR ? (
- {t("steps.approve_error")}
+ {txState.meta.signError?.message || t("steps.approve_error")}
) : (
diff --git a/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx b/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx
index a41c2ebf..b48a4f87 100644
--- a/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx
+++ b/packages/widget/src/pages/steps/pages/unstake-steps.page.tsx
@@ -1,15 +1,41 @@
+import { useUnstakeOrPendingActionParams } from "@sk-widget/hooks/navigation/use-unstake-or-pending-action-params";
+import { usePositionBalances } from "@sk-widget/hooks/use-position-balances";
+import { useProvidersDetails } from "@sk-widget/hooks/use-provider-details";
import { useExitStakeStore } from "@sk-widget/providers/exit-stake-store";
import { useSelector } from "@xstate/store/react";
+import { Maybe } from "purify-ts";
+import { useMemo } from "react";
import { useTrackPage } from "../../../hooks/tracking/use-track-page";
import { StepsPage } from "./common.page";
export const UnstakeStepsPage = () => {
+ useTrackPage("unstakeSteps");
+
const exitRequest = useSelector(
useExitStakeStore(),
(state) => state.context.data
).unsafeCoerce();
- useTrackPage("unstakeSteps");
+ const { plain } = useUnstakeOrPendingActionParams();
+ const positionBalances = usePositionBalances({
+ balanceId: plain.balanceId,
+ integrationId: plain.integrationId,
+ });
+
+ const providersDetails = useProvidersDetails({
+ integrationData: useMemo(
+ () => Maybe.of(exitRequest.integrationData),
+ [exitRequest.integrationData]
+ ),
+ validatorsAddresses: positionBalances.data.map((p) =>
+ p.type === "validators" ? p.validatorsAddresses : []
+ ),
+ });
- return ;
+ return (
+
+ );
};
diff --git a/packages/widget/src/providers/settings.tsx b/packages/widget/src/providers/settings.tsx
index 366c60b7..35c80387 100644
--- a/packages/widget/src/providers/settings.tsx
+++ b/packages/widget/src/providers/settings.tsx
@@ -44,6 +44,7 @@ export type SettingsProps = {
disableInjectedProviderDiscovery?: boolean;
mapWalletFn?: Parameters[0]["mapWalletFn"];
customTranslations?: RecursivePartial;
+ tokensForEnabledYieldsOnly?: boolean;
};
export type SettingsContextType = SettingsProps & VariantProps;
diff --git a/packages/widget/src/providers/sk-wallet/errors.ts b/packages/widget/src/providers/sk-wallet/errors.ts
index 67868bc6..2418f6b6 100644
--- a/packages/widget/src/providers/sk-wallet/errors.ts
+++ b/packages/widget/src/providers/sk-wallet/errors.ts
@@ -7,3 +7,24 @@ export class SafeFailedError extends Error {
this.type = type;
}
}
+
+export class SendTransactionError extends Error {
+ _tag = "SendTransactionError";
+
+ constructor(message?: string) {
+ super(message);
+
+ this._tag = "SendTransactionError";
+ }
+}
+export class TransactionDecodeError extends Error {
+ _tag = "TransactionDecodeError";
+
+ constructor(message?: string) {
+ super(message);
+
+ this._tag = "TransactionDecodeError";
+ }
+}
+
+new SendTransactionError();
diff --git a/packages/widget/src/providers/sk-wallet/index.tsx b/packages/widget/src/providers/sk-wallet/index.tsx
index a8316cd5..18f85875 100644
--- a/packages/widget/src/providers/sk-wallet/index.tsx
+++ b/packages/widget/src/providers/sk-wallet/index.tsx
@@ -5,11 +5,18 @@ import {
isEvmChain,
isSolanaChain,
isTonChain,
+ isTronChain,
} from "@sk-widget/domain/types/chains";
-import type { SKTx } from "@sk-widget/domain/types/wallets/generic-wallet";
+import type {
+ SKTx,
+ TronTx,
+} from "@sk-widget/domain/types/wallets/generic-wallet";
import { useCheckIsUnmounted } from "@sk-widget/hooks/use-check-is-unmounted";
import { isSafeConnector } from "@sk-widget/providers/safe/safe-connector-meta";
-import { SafeFailedError } from "@sk-widget/providers/sk-wallet/errors";
+import {
+ SafeFailedError,
+ SendTransactionError,
+} from "@sk-widget/providers/sk-wallet/errors";
import { useInit } from "@sk-widget/providers/sk-wallet/use-init";
import { Either, EitherAsync, Left, Maybe, Right } from "purify-ts";
import type { PropsWithChildren } from "react";
@@ -30,17 +37,13 @@ import {
import type { SKWallet } from "../../domain/types";
import { useTrackEvent } from "../../hooks/tracking/use-track-event";
import { useIsomorphicEffect } from "../../hooks/use-isomorphic-effect";
-import {
- type NotSupportedFlowError,
- SendTransactionError,
- TransactionDecodeError,
-} from "../../pages/steps/hooks/errors";
import { isLedgerDappBrowserProvider } from "../../utils";
import { isCosmosConnector } from "../cosmos/cosmos-connector-meta";
import { isExternalProviderConnector } from "../external-provider";
import { isLedgerLiveConnector } from "../ledger/ledger-live-connector-meta";
import { isTronConnector } from "../misc/tron-connector-meta";
import { useWagmiConfig } from "../wagmi";
+import { TransactionDecodeError } from "./errors";
import { useAdditionalAddresses } from "./use-additional-addresses";
import { useConnectorChains } from "./use-connector-chains";
import { useCosmosCW } from "./use-cosmos-cw";
@@ -163,215 +166,239 @@ export const SKWalletProvider = ({ children }: PropsWithChildren) => {
const signTransaction = useCallback(
({ tx, ledgerHwAppId, txMeta, network }) =>
- connectorDetails.chain<
- TransactionDecodeError | SendTransactionError | NotSupportedFlowError,
- { signedTx: string; broadcasted: boolean }
- >(({ conn, address }) => {
- /**
- * Ledger Live connector
- */
- if (isLedgerLiveConnector(conn)) {
- return EitherAsync.liftEither(
- Maybe.fromNullable(ledgerCurrentAccountId).toEither(
- new Error("currentAccountId missing")
+ connectorDetails
+ .mapLeft(() => new SendTransactionError())
+ .chain<
+ TransactionDecodeError | SendTransactionError,
+ { signedTx: string; broadcasted: boolean }
+ >(({ conn, address }) => {
+ /**
+ * Ledger Live connector
+ */
+ if (isLedgerLiveConnector(conn)) {
+ return EitherAsync.liftEither(
+ Maybe.fromNullable(ledgerCurrentAccountId).toEither(
+ new SendTransactionError()
+ )
)
- )
- .chain((val) =>
- EitherAsync.liftEither(
- Either.encase(() => JSON.parse(tx))
- .mapLeft(() => new Error("JSON.parse failed"))
- .chain((parsedTx) =>
- Either.encase(() =>
- conn.deserializeTransaction(parsedTx)
- ).mapLeft(() => new Error("deserializeTransaction failed"))
+ .chain((val) =>
+ EitherAsync.liftEither(
+ Either.encase(() => JSON.parse(tx))
+
+ .chain((parsedTx) =>
+ Either.encase(() => conn.deserializeTransaction(parsedTx))
+ )
+ .mapLeft(() => new TransactionDecodeError())
+ ).map((deserializedTransaction) => ({
+ accountId: val,
+ deserializedTransaction,
+ }))
+ )
+ .chain(({ accountId, deserializedTransaction }) =>
+ EitherAsync(() =>
+ conn.walletApiClient.transaction.signAndBroadcast(
+ accountId,
+ deserializedTransaction,
+ Maybe.fromNullable(ledgerHwAppId)
+ .map((v) => ({ hwAppId: v }))
+ .extract()
)
- ).map((deserializedTransaction) => ({
- accountId: val,
- deserializedTransaction,
- }))
+ ).mapLeft((e) => {
+ console.log(e);
+ return new SendTransactionError();
+ })
+ )
+ .map((val) => ({ signedTx: val, broadcasted: true }));
+ }
+
+ /**
+ * Cosmos connector
+ */
+ if (isCosmosConnector(conn)) {
+ return EitherAsync.liftEither(
+ Maybe.fromNullable(cosmosCW).toEither(
+ new Error("cosmosCW missing")
+ )
)
- .chain(({ accountId, deserializedTransaction }) =>
- EitherAsync(() =>
- conn.walletApiClient.transaction.signAndBroadcast(
- accountId,
- deserializedTransaction,
- Maybe.fromNullable(ledgerHwAppId)
- .map((v) => ({ hwAppId: v }))
- .extract()
- )
- ).mapLeft((e) => {
- console.log(e);
- return new Error("sign failed");
- })
+ .chain((cw) =>
+ // We need to sign + broadcast as `walletconnect` cosmos client does not support `sendTx`
+ conn
+ .signTransaction({ cw, tx })
+ .map((val) => ({ signedTx: val, broadcasted: false }))
+ )
+ .mapLeft(() => new SendTransactionError());
+ }
+
+ /**
+ * Tron connector
+ */
+ if (isTronConnector(conn)) {
+ return EitherAsync.liftEither(
+ Either.encase(() => JSON.parse(tx))
+ .chain((val) => unsignedTronTransactionCodec.decode(val))
+ .mapLeft((e) => {
+ console.log(e);
+ return new TransactionDecodeError();
+ })
)
- .map((val) => ({ signedTx: val, broadcasted: true }));
- }
+ .chain((val) =>
+ EitherAsync(() => conn.signTransaction(val)).mapLeft((e) => {
+ console.log(e);
+ return new SendTransactionError();
+ })
+ )
+ .map((val) => ({
+ signedTx: JSON.stringify(val),
+ broadcasted: false,
+ }));
+ }
- /**
- * Cosmos connector
- */
- if (isCosmosConnector(conn)) {
- return EitherAsync.liftEither(
- Maybe.fromNullable(cosmosCW).toEither(new Error("cosmosCW missing"))
- ).chain((cw) =>
- // We need to sign + broadcast as `walletconnect` cosmos client does not support `sendTx`
- conn
- .signTransaction({ cw, tx })
- .map((val) => ({ signedTx: val, broadcasted: false }))
- );
- }
+ /**
+ * External provider connector
+ */
+ if (isExternalProviderConnector(conn)) {
+ return EitherAsync.liftEither(
+ Right(null)
+ .chain(() => {
+ if (isEvmChain(network)) {
+ return Either.encase(() => JSON.parse(tx))
+ .mapLeft(() => "Failed to parse tx")
+ .chain((val) => unsignedEVMTransactionCodec.decode(val))
+ .map((v) => prepareEVMTx({ address, decodedTx: v }));
+ }
+
+ if (isSolanaChain(network)) {
+ return unsignedSolanaTransactionCodec
+ .decode(tx)
+ .map((v) => ({ type: "solana", tx: v }));
+ }
+
+ if (isTonChain(network)) {
+ return Either.encase(() => JSON.parse(tx))
+ .mapLeft(() => "Failed to parse tx")
+ .chain((val) => unsignedTonTransactionCodec.decode(val))
+ .map((v) => ({ type: "ton", tx: v }));
+ }
+
+ if (isTronChain(network)) {
+ return Either.encase(() => JSON.parse(tx))
+ .mapLeft(() => "Failed to parse tx")
+ .chain((val) => unsignedTronTransactionCodec.decode(val))
+ .map((v) => ({ type: "tron", tx: v }) as TronTx);
+ }
+
+ return Left("Unsupported network");
+ })
+ .mapLeft((e) => {
+ console.log(e);
+ return new TransactionDecodeError();
+ })
+ )
+ .chain((val) =>
+ conn
+ .sendTransaction(val, txMeta)
+ .mapLeft(
+ (e) =>
+ new SendTransactionError(
+ typeof e === "string" ? e : undefined
+ )
+ )
+ )
+ .map((val) => ({ signedTx: val, broadcasted: true }));
+ }
- /**
- * Tron connector
- */
- if (isTronConnector(conn)) {
- return EitherAsync.liftEither(
- Either.encase(() => JSON.parse(tx))
- .chain((val) => unsignedTronTransactionCodec.decode(val))
- .mapLeft((e) => {
- console.log(e);
- return new TransactionDecodeError();
- })
- )
- .chain((val) =>
- EitherAsync(() => conn.signTransaction(val)).mapLeft((e) => {
- console.log(e);
- return new Error("sign failed");
- })
+ /**
+ * Safe connector
+ */
+ if (isSafeConnector(conn)) {
+ return EitherAsync.liftEither(
+ Either.encase(() => JSON.parse(tx))
+ .chain((val) => unsignedEVMTransactionCodec.decode(val))
+ .map((val) => prepareEVMTx({ address, decodedTx: val }))
+ .mapLeft(() => new TransactionDecodeError())
)
- .map((val) => ({
- signedTx: JSON.stringify(val),
- broadcasted: false,
- }));
- }
-
- /**
- * External provider connector
- */
- if (isExternalProviderConnector(conn)) {
+ .chain(({ tx }) =>
+ conn
+ .sendTransactions({
+ txs: [
+ {
+ data: tx.data,
+ to: tx.to,
+ value: tx.value ?? "0",
+ },
+ ],
+ })
+ .map((res) => res.safeTxHash)
+ )
+ .chain((safeTxHash) =>
+ withRequestErrorRetry({
+ fn: () =>
+ conn
+ .getTxStatus(safeTxHash)
+ .chain((res) =>
+ !res.txHash || res.txStatus !== conn.txStatus.SUCCESS
+ ? EitherAsync.liftEither(
+ Left(
+ new SafeFailedError(
+ res.txStatus === conn.txStatus.FAILED ||
+ res.txStatus === conn.txStatus.CANCELLED
+ ? "FAILED"
+ : "NOT_READY"
+ )
+ )
+ )
+ : EitherAsync.liftEither(Right(res.txHash))
+ )
+ .run()
+ .then((res) => res.unsafeCoerce()),
+ shouldRetry: (error, retryCount) =>
+ Maybe.fromNullable(error)
+ .chainNullable((e) =>
+ (e as SafeFailedError)._tag === "SafeFailedError"
+ ? (e as SafeFailedError)
+ : null
+ )
+ .filter((e) => e.type !== "FAILED" && !checkIsUnmounted())
+ .map(() => retryCount < 120)
+ .orDefault(false),
+ retryWaitForMs: () => 7000,
+ })
+ )
+ .mapLeft(() => new SendTransactionError())
+ .map((val) => ({ signedTx: val as Hash, broadcasted: true }));
+ }
+
+ /**
+ * EVM connector
+ */
return EitherAsync.liftEither(
- Right(null)
- .chain(() => {
- if (isEvmChain(network)) {
- return Either.encase(() => JSON.parse(tx))
- .mapLeft(() => "Failed to parse tx")
- .chain((val) => unsignedEVMTransactionCodec.decode(val))
- .map((v) => prepareEVMTx({ address, decodedTx: v }));
- }
-
- if (isSolanaChain(network)) {
- return unsignedSolanaTransactionCodec.decode(tx);
- }
-
- if (isTonChain(network)) {
- return Either.encase(() => JSON.parse(tx))
- .mapLeft(() => "Failed to parse tx")
- .chain((val) => unsignedTonTransactionCodec.decode(val));
- }
-
- return Left("Unsupported network");
- })
+ Either.encase(() => JSON.parse(tx))
+ .chain((val) => unsignedEVMTransactionCodec.decode(val))
.mapLeft((e) => {
console.log(e);
return new TransactionDecodeError();
})
- )
- .chain((val) => conn.sendTransaction(val, txMeta))
- .map((val) => ({ signedTx: val, broadcasted: true }));
- }
-
- /**
- * Safe connector
- */
- if (isSafeConnector(conn)) {
- return EitherAsync.liftEither(
- Either.encase(() => JSON.parse(tx))
- .chain((val) => unsignedEVMTransactionCodec.decode(val))
- .map((val) => prepareEVMTx({ address, decodedTx: val }))
- .mapLeft(() => new TransactionDecodeError())
- )
- .chain(({ tx }) =>
- conn
- .sendTransactions({
- txs: [
- {
- data: tx.data,
- to: tx.to,
- value: tx.value ?? "0",
- },
- ],
- })
- .map((res) => res.safeTxHash)
- )
- .chain((safeTxHash) =>
- withRequestErrorRetry({
- fn: () =>
- conn
- .getTxStatus(safeTxHash)
- .chain((res) =>
- !res.txHash || res.txStatus !== conn.txStatus.SUCCESS
- ? EitherAsync.liftEither(
- Left(
- new SafeFailedError(
- res.txStatus === conn.txStatus.FAILED ||
- res.txStatus === conn.txStatus.CANCELLED
- ? "FAILED"
- : "NOT_READY"
- )
- )
- )
- : EitherAsync.liftEither(Right(res.txHash))
- )
- .run()
- .then((res) => res.unsafeCoerce()),
- shouldRetry: (error, retryCount) =>
- Maybe.fromNullable(error)
- .chainNullable((e) =>
- (e as SafeFailedError)._tag === "SafeFailedError"
- ? (e as SafeFailedError)
- : null
- )
- .filter((e) => e.type !== "FAILED" && !checkIsUnmounted())
- .map(() => retryCount < 120)
- .orDefault(false),
- retryWaitForMs: () => 7000,
+ ).chain((val) =>
+ EitherAsync(() =>
+ /**
+ * Params need to be in strict format, don't spread the object(val)!
+ */
+ sendTransactionAsync({
+ data: val.data,
+ to: val.to,
+ value: val.value,
+ nonce: val.nonce,
+ maxFeePerGas: val.maxFeePerGas,
+ maxPriorityFeePerGas: val.maxPriorityFeePerGas,
+ chainId: val.chainId,
+ gas: val.gasLimit,
+ type: val.maxFeePerGas ? "eip1559" : "legacy",
})
)
- .mapLeft(() => new Error("sendTransactions failed"))
- .map((val) => ({ signedTx: val as Hash, broadcasted: true }));
- }
-
- /**
- * EVM connector
- */
- return EitherAsync.liftEither(
- Either.encase(() => JSON.parse(tx))
- .chain((val) => unsignedEVMTransactionCodec.decode(val))
- .mapLeft((e) => {
- console.log(e);
- return new TransactionDecodeError();
- })
- ).chain((val) =>
- EitherAsync(() =>
- /**
- * Params need to be in strict format, don't spread the object(val)!
- */
- sendTransactionAsync({
- data: val.data,
- to: val.to,
- value: val.value,
- nonce: val.nonce,
- maxFeePerGas: val.maxFeePerGas,
- maxPriorityFeePerGas: val.maxPriorityFeePerGas,
- chainId: val.chainId,
- gas: val.gasLimit,
- type: val.maxFeePerGas ? "eip1559" : "legacy",
- })
- )
- .mapLeft(() => new SendTransactionError())
- .map((val) => ({ signedTx: val, broadcasted: true }))
- );
- }),
+ .mapLeft(() => new SendTransactionError())
+ .map((val) => ({ signedTx: val, broadcasted: true }))
+ );
+ }),
[
connectorDetails,
cosmosCW,
diff --git a/packages/widget/src/providers/sk-wallet/validation.ts b/packages/widget/src/providers/sk-wallet/validation.ts
index 4c094e18..d41a3f7f 100644
--- a/packages/widget/src/providers/sk-wallet/validation.ts
+++ b/packages/widget/src/providers/sk-wallet/validation.ts
@@ -1,10 +1,15 @@
-import type {
- SolanaTx,
- TonTx,
-} from "@sk-widget/domain/types/wallets/generic-wallet";
-import type { Transaction as TronTx } from "@tronweb3/tronwallet-abstract-adapter";
import type { GetType } from "purify-ts";
-import { Codec, Left, Right, number, optional } from "purify-ts";
+import {
+ Codec,
+ Left,
+ Right,
+ boolean,
+ number,
+ optional,
+ record,
+ string,
+ unknown,
+} from "purify-ts";
import type { Address, Hex } from "viem";
const bigintCodec = Codec.custom({
@@ -51,43 +56,16 @@ export const unsignedEVMTransactionCodec = Codec.interface({
export type DecodedEVMTransaction = GetType;
-export const unsignedTronTransactionCodec = Codec.custom({
- decode(value) {
- const val = value as Partial;
-
- if (val.raw_data && val.raw_data_hex && val.txID && "visible" in val) {
- return Right(val as TronTx);
- }
- return Left("Invalid Tron transaction");
- },
- encode(value) {
- return value;
- },
+export const unsignedTronTransactionCodec = Codec.interface({
+ raw_data: record(string, unknown),
+ raw_data_hex: string,
+ txID: string,
+ visible: boolean,
});
-export const unsignedSolanaTransactionCodec = Codec.custom({
- decode(value) {
- if (typeof value !== "string") {
- return Left("Invalid solana transaction");
- }
+export const unsignedSolanaTransactionCodec = string;
- return Right({ type: "solana", tx: value } as SolanaTx);
- },
- encode(value) {
- return value;
- },
-});
-
-export const unsignedTonTransactionCodec = Codec.custom({
- decode(value) {
- const val = value as Partial;
-
- if (typeof val.seqno === "number" && typeof val.message === "string") {
- return Right({ type: "ton", tx: val } as TonTx);
- }
- return Left("Invalid TON transaction");
- },
- encode(value) {
- return value;
- },
+export const unsignedTonTransactionCodec = Codec.interface({
+ seqno: bigintCodec,
+ message: string,
});
diff --git a/packages/widget/src/providers/tracking/index.tsx b/packages/widget/src/providers/tracking/index.tsx
index eaad91da..43fe2e97 100644
--- a/packages/widget/src/providers/tracking/index.tsx
+++ b/packages/widget/src/providers/tracking/index.tsx
@@ -55,6 +55,7 @@ const trackEventMap = {
validatorsSubmitted: "Validators submitted",
validatorImported: "Validator imported",
viewTxClicked: "View transaction clicked",
+ actionStepsCancelled: "Action steps cancelled",
initYield: "system/initYield",
initToken: "system/initToken",
} as const;
diff --git a/packages/widget/src/translation/English/translations.json b/packages/widget/src/translation/English/translations.json
index a229db4c..e8379b25 100644
--- a/packages/widget/src/translation/English/translations.json
+++ b/packages/widget/src/translation/English/translations.json
@@ -211,7 +211,9 @@
"LUGANODES_EXIT_REQUEST": "LUGANODES EXIT REQUEST",
"INFSTONES_PROVISION": "INFSTONES PROVISION",
"INFSTONES_EXIT_REQUEST": "INFSTONES EXIT REQUEST",
- "INFSTONES_CLAIM_REQUEST": "INFSTONES CLAIM REQUEST"
+ "INFSTONES_CLAIM_REQUEST": "INFSTONES CLAIM REQUEST",
+ "WRAP": "WRAP",
+ "UNWRAP": "UNWRAP"
}
},
"complete": {
diff --git a/packages/widget/src/translation/French/translations.json b/packages/widget/src/translation/French/translations.json
index 25a95415..f01af047 100644
--- a/packages/widget/src/translation/French/translations.json
+++ b/packages/widget/src/translation/French/translations.json
@@ -211,7 +211,9 @@
"LUGANODES_EXIT_REQUEST": "DEMANDE DE SORTIE LUGANODES",
"INFSTONES_PROVISION": "FOURNITURE INFSTONES",
"INFSTONES_EXIT_REQUEST": "DEMANDE DE SORTIE INFSTONES",
- "INFSTONES_CLAIM_REQUEST": "DEMANDE DE RÉCLAMATION INFSTONES"
+ "INFSTONES_CLAIM_REQUEST": "DEMANDE DE RÉCLAMATION INFSTONES",
+ "WRAP": "ENVELOPER",
+ "UNWRAP": "DÉENVELOPER"
}
},
"complete": {
diff --git a/packages/widget/tests/use-cases/sk-wallet.test.tsx b/packages/widget/tests/use-cases/sk-wallet.test.tsx
index 90536eac..01b42026 100644
--- a/packages/widget/tests/use-cases/sk-wallet.test.tsx
+++ b/packages/widget/tests/use-cases/sk-wallet.test.tsx
@@ -72,6 +72,15 @@ describe("SK Wallet", () => {
actionId: "",
actionType: "STAKE",
txType: "APPROVAL",
+ amount: "100",
+ inputToken: {
+ address: "",
+ decimals: 0,
+ symbol: "",
+ name: "",
+ network: "solana",
+ },
+ providersDetails: [],
},
ledgerHwAppId: null,
});
@@ -90,6 +99,15 @@ describe("SK Wallet", () => {
actionId: "",
actionType: "STAKE",
txType: "APPROVAL",
+ amount: "100",
+ inputToken: {
+ address: "",
+ decimals: 0,
+ symbol: "",
+ name: "",
+ network: "solana",
+ },
+ providersDetails: [],
}
);
});
@@ -128,6 +146,15 @@ describe("SK Wallet", () => {
actionId: "",
actionType: "STAKE",
txType: "APPROVAL",
+ amount: "100",
+ inputToken: {
+ address: "",
+ decimals: 0,
+ symbol: "",
+ name: "",
+ network: "ton",
+ },
+ providersDetails: [],
},
ledgerHwAppId: null,
});
@@ -139,13 +166,22 @@ describe("SK Wallet", () => {
expect(sendTransactionSpy).toHaveBeenCalledWith(
{
type: "ton",
- tx: { seqno: 0, message: "12345" },
+ tx: { seqno: 0n, message: "12345" },
},
{
txId: "",
actionId: "",
actionType: "STAKE",
txType: "APPROVAL",
+ amount: "100",
+ inputToken: {
+ address: "",
+ decimals: 0,
+ symbol: "",
+ name: "",
+ network: "ton",
+ },
+ providersDetails: [],
}
);
});
diff --git a/packages/widget/tests/use-cases/staking-flow/setup.ts b/packages/widget/tests/use-cases/staking-flow/setup.ts
index bafad682..9a6f6431 100644
--- a/packages/widget/tests/use-cases/staking-flow/setup.ts
+++ b/packages/widget/tests/use-cases/staking-flow/setup.ts
@@ -132,6 +132,7 @@ export const setup = async () => {
validatorAddresses: null,
completedAt: null,
createdAt: "2023-12-28T14:36:21.700Z",
+ projectId: "projectId",
transactions: [
{
id: "9aa80e02-3d81-4b0f-aa0b-155138b77293",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e711fd60..6d81b3e7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -185,8 +185,8 @@ importers:
specifier: ^9.1.0
version: 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.24.2)
'@stakekit/api-hooks':
- specifier: 0.0.98
- version: 0.0.98(@faker-js/faker@9.7.0)(@tanstack/react-query@5.74.0(react@19.1.0))(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(react@19.1.0)
+ specifier: 0.0.100
+ version: 0.0.100(@faker-js/faker@9.7.0)(@tanstack/react-query@5.74.0(react@19.1.0))(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(react@19.1.0)
'@stakekit/common':
specifier: ^0.0.48
version: 0.0.48
@@ -2538,8 +2538,8 @@ packages:
'@stablelib/x25519@1.0.3':
resolution: {integrity: sha512-KnTbKmUhPhHavzobclVJQG5kuivH+qDLpe84iRqX3CLrKp881cF160JvXJ+hjn1aMyCwYOKeIZefIH/P5cJoRw==}
- '@stakekit/api-hooks@0.0.98':
- resolution: {integrity: sha512-QxHYi7JnoU/FYViUp27zk+JhUAu37ziKQXfr9BsUQb5weza46jjIEy+B29T/cc7BH8iHtA+R7I2OiCG10IEiTQ==}
+ '@stakekit/api-hooks@0.0.100':
+ resolution: {integrity: sha512-vXvZHDU6Wzkt6349D86gw2S7YqgzmvMzQdjXglkdsfVP6p6tz02BJxkZ8x+nIKwRwN9WteIHmIFUwPJiybHUsA==}
peerDependencies:
'@faker-js/faker': ^9
'@tanstack/react-query': '>=5'
@@ -9025,7 +9025,7 @@ snapshots:
'@stablelib/random': 1.0.2
'@stablelib/wipe': 1.0.1
- '@stakekit/api-hooks@0.0.98(@faker-js/faker@9.7.0)(@tanstack/react-query@5.74.0(react@19.1.0))(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(react@19.1.0)':
+ '@stakekit/api-hooks@0.0.100(@faker-js/faker@9.7.0)(@tanstack/react-query@5.74.0(react@19.1.0))(msw@2.7.4(@types/node@22.14.1)(typescript@5.8.3))(react@19.1.0)':
dependencies:
react: 19.1.0
optionalDependencies: