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/angry-beans-yawn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stakekit/widget": patch
---

feat: tokens for enabled yields only option
5 changes: 5 additions & 0 deletions .changeset/clean-teeth-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stakekit/widget": patch
---

feat(externalProviders): add tron support
5 changes: 5 additions & 0 deletions .changeset/six-rats-return.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stakekit/widget": patch
---

feat(externalProviders): display signTransaction message
5 changes: 5 additions & 0 deletions .changeset/sour-papers-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stakekit/widget": patch
---

feat(externalProviders): provider details in txMeta
5 changes: 5 additions & 0 deletions .changeset/stupid-lights-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stakekit/widget": patch
---

fix(bundledWidget): allow multiple re-renders
5 changes: 5 additions & 0 deletions .changeset/three-garlics-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@stakekit/widget": patch
---

feat(tracking): add action steps cancelled event
2 changes: 1 addition & 1 deletion packages/widget/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 4 additions & 3 deletions packages/widget/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 <SKApp {...props} />;
Expand All @@ -269,6 +269,7 @@ export const renderSKWidget = ({
root.render(<BundledSKWidget {...rest} ref={appRef} />);

return {
rerender: (newProps: SKAppProps) => appRef.current.rerender(newProps),
rerender: (newProps: SKAppProps) =>
appRef.current.rerender({ ...newProps, ref: appRef }),
};
};
9 changes: 8 additions & 1 deletion packages/widget/src/common/get-token-balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/widget/src/domain/types/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) ||
Expand Down
47 changes: 22 additions & 25 deletions packages/widget/src/domain/types/external-providers.ts
Original file line number Diff line number Diff line change
@@ -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<SKExternalProviders>) {}

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 }) {
Expand Down
15 changes: 4 additions & 11 deletions packages/widget/src/domain/types/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,25 @@
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,
} from "@stakekit/api-hooks";
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;

export type SKWallet = {
disconnect: () => Promise<void>;
signTransaction: (args: {
tx: NonNullable<TransactionDto["unsignedTransaction"]>;
txMeta: {
txId: TransactionDto["id"];
actionId: ActionDto["id"];
actionType: ActionDto["type"];
txType: TransactionDto["type"];
};
txMeta: SKTxMeta;
ledgerHwAppId: Nullable<string>;
network: Networks;
}) => EitherAsync<
Expand Down
53 changes: 40 additions & 13 deletions packages/widget/src/domain/types/wallets/generic-wallet.ts
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -29,29 +32,53 @@ 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<string>;
switchChain: (chainId: number) => Promise<void>;
getTransactionReceipt?(txHash: string): Promise<{ transactionHash?: string }>;
sendTransaction(
tx: SKTx,
txMeta: {
txId: TransactionDto["id"];
actionId: ActionDto["id"];
actionType: ActionDto["type"];
txType: TransactionDto["type"];
}
): Promise<string>;
txMeta: SKTxMeta
): Promise<
| string
| { type: "success"; txHash: string }
| { type: "error"; error: string }
>;
};
31 changes: 22 additions & 9 deletions packages/widget/src/hooks/api/use-default-tokens.ts
Original file line number Diff line number Diff line change
@@ -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,
});
};
Expand All @@ -33,10 +43,13 @@ export const getDefaultTokens = (

const queryFn = ({
network,
}: {
network: SKWallet["network"];
}) =>
EitherAsync(() => tokenGetTokens({ network: network ?? undefined })).map(
(val) =>
val.map<TokenBalanceScanResponseDto>((v) => ({ ...v, amount: "0" }))
enabledYieldsOnly,
}: Pick<TokenGetTokensParams, "network" | "enabledYieldsOnly">) =>
EitherAsync(() =>
tokenGetTokens({
network,
enabledYieldsOnly: enabledYieldsOnly || undefined,
})
).map((val) =>
val.map<TokenBalanceScanResponseDto>((v) => ({ ...v, amount: "0" }))
);
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -41,6 +41,7 @@ export const useInitToken = () => {
address,
network,
queryClient,
tokensForEnabledYieldsOnly,
}).chain((val) =>
getInitParams({
isLedgerLive,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -44,6 +44,7 @@ export const useInitYield = ({
address,
network,
queryClient,
tokensForEnabledYieldsOnly,
})
.chain((val) =>
EitherAsync.liftEither(
Expand Down
Loading