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
42 changes: 22 additions & 20 deletions apps/web-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
"private": true,
"type": "module",
"dependencies": {
"@i18next-selector/vite-plugin": "^0.0.18",
"@i18next-selector/vite-plugin": "0.0.18",
"@privy-io/react-auth": "3.0.1",
"@privy-io/wagmi": "2.0.0",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-checkbox": "1.3.3",
"@radix-ui/react-collapsible": "1.1.12",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-dropdown-menu": "2.1.16",
"@radix-ui/react-label": "2.1.7",
"@radix-ui/react-select": "2.2.6",
"@radix-ui/react-slider": "1.3.6",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-switch": "1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"@radix-ui/react-tabs": "1.1.13",
"@radix-ui/react-tooltip": "1.2.8",
"@sovryn/slayer-sdk": "workspace:*",
"@sovryn/slayer-shared": "workspace:*",
"@tailwindcss/vite": "4.1.13",
"@tanstack/devtools-vite": "^0.3.3",
"@tanstack/devtools-vite": "0.3.3",
"@tanstack/react-devtools": "0.7.0",
"@tanstack/react-form": "1.23.0",
"@tanstack/react-query": "5.90.2",
Expand All @@ -30,27 +30,29 @@
"@tanstack/router-plugin": "1.131.50",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"debug": "^4.4.3",
"debug": "4.4.3",
"envalid": "8.1.0",
"i18next": "^25.5.2",
"i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
"i18next": "25.5.2",
"i18next-browser-languagedetector": "8.2.0",
"i18next-http-backend": "3.0.2",
"lucide-react": "0.544.0",
"next-themes": "^0.4.6",
"next-themes": "0.4.6",
"react": "19.1.1",
"react-dom": "19.1.1",
"react-i18next": "^15.7.3",
"recharts": "^3.2.1",
"sonner": "^2.0.7",
"react-i18next": "15.7.3",
"recharts": "3.2.1",
"sonner": "2.0.7",
"tailwind-merge": "3.3.1",
"tailwindcss": "4.1.13",
"tw-animate-css": "1.3.8",
"viem": "2.37.8",
"wagmi": "2.17.2",
"zod": "4.1.11"
"use-sync-external-store": "1.6.0",
"viem": "2.38.6",
"wagmi": "2.19.2",
"zod": "4.1.11",
"zustand": "5.0.8"
},
"devDependencies": {
"@types/debug": "^4.1.12",
"@types/debug": "4.1.12",
"vite": "7.1.7",
"vite-plugin-node-polyfills": "0.24.0",
"web-vitals": "5.1.0"
Expand Down
6 changes: 5 additions & 1 deletion apps/web-app/public/locales/en/common.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"accept": "Accept",
"cancel": "Cancel",
"close": "Close"
"close": "Close",
"confirm": "Confirm",
"continue": "Continue",
"abort": "Abort",
"loading": "Loading..."
}
10 changes: 10 additions & 0 deletions apps/web-app/public/locales/en/tx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"title": "Transaction Confirmation",
"description": "Please review and confirm transactions in your wallet",
"preparing": "Preparing transaction...",
"connectWallet": "Connect your wallet to proceed.",
"switchNetwork": "Switch to {{name}} network",
"signMessage": "Sign Message",
"signTypedData": "Sign Typed Data",
"sendTransaction": "Send Transaction"
}
2 changes: 2 additions & 0 deletions apps/web-app/src/@types/i18next.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { resources as common } from 'public/locales/en/common';
import type { resources as glossary } from 'public/locales/en/glossary';
import type { resources as tx } from 'public/locales/en/tx';
import type { resources as validation } from 'public/locales/en/validation';
import { defaultNS } from '../i18n';

Expand All @@ -11,6 +12,7 @@ declare module 'i18next' {
common: typeof common;
glossary: typeof glossary;
validation: typeof validation;
tx: typeof tx;
};
}
}
65 changes: 65 additions & 0 deletions apps/web-app/src/components/LinkToExplorer/LinkToExplorer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useMemo } from 'react';
import type { Address, Chain, Hash } from 'viem';
import { useChains } from 'wagmi';

type ChainProps =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create LinkToExplorer.types.ts and put the type definitions there (except LinkToExplorerProps).

Copy link
Contributor Author

@creed-victor creed-victor Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think moving it to another file just increases compilation time but does not add any benefit as these types aren't supposed to be used anywhere else atm

| {
chainId: number;
}
| {
chain: Chain;
};

type ValueProps =
| {
address: Address;
}
| {
txHash: Hash;
};

type LinkToExplorerProps = ValueProps &
ChainProps & {
className?: string;
};

export const LinkToExplorer = (props: LinkToExplorerProps) => {
const chains = useChains();

const chain = useMemo(() => {
if ('chain' in props) {
return props.chain;
}
return chains.find((c) => c.id === props.chainId);
}, [props, chains]);

const path = useMemo(() => {
if ('address' in props) {
return `/address/${props.address}`;
}
if ('txHash' in props) {
return `/tx/${props.txHash}`;
}
return '/';
}, [props]);

const title = useMemo(() => {
if ('address' in props) {
return props.address;
}
if ('txHash' in props) {
return props.txHash;
}
}, [props, chain]);

return (
<a
href={chain?.blockExplorers?.default.url + path}
className={props.className}
target="_blank"
rel="noopener noreferrer"
>
{title?.slice(0, 6)}...{title?.slice(-4)}
</a>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ import { AmountRenderer } from '@/components/ui/amount-renderer';
import { Button } from '@/components/ui/button';
import { InfoButton } from '@/components/ui/info-button';
import { sdk } from '@/lib/sdk';
import {
BorrowRateMode,
type MoneyMarketPoolReserve,
type Token,
} from '@sovryn/slayer-sdk';
import { useSlayerTx } from '@/lib/transactions';
import { type MoneyMarketPoolReserve, type Token } from '@sovryn/slayer-sdk';
import { Decimal } from '@sovryn/slayer-shared';
import { useAccount, useWriteContract } from 'wagmi';

Expand All @@ -35,22 +32,31 @@ export const AssetsTable: FC<AssetsTableProps> = ({ assets }) => {

const { writeContractAsync } = useWriteContract();

const { begin } = useSlayerTx();

const handleBorrow = async (token: Token) => {
const msg = await sdk.moneyMarket.borrow(
token,
Decimal.from(1),
BorrowRateMode.stable,
{
begin(async () => {
const s = await sdk.moneyMarket.borrow(token, Decimal.from(1), 1, {
account: address!,
},
);
console.log('Transaction Request:', msg);
});
console.log('Transaction Request:', s);
return s;
});

// const msg = await sdk.moneyMarket.borrow(
// token,
// Decimal.from(1),
// BorrowRateMode.stable,
// {
// account: address!,
// },
// );
// console.log('Transaction Request:', msg);

if (msg.length) {
const { chain, ...contractParams } = msg[0];
const data = await writeContractAsync(contractParams);
console.log('Transaction Response:', data);
}
// if (msg.length) {
// // const data = await writeContractAsync<any>(msg[0]);
// // console.log('Transaction Response:', data);
// }
// const d = await signMessageAsync(msg);
// console.warn('Signature:', { data, d });
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { txStore } from '@/lib/transactions/store';
import clsx from 'clsx';
import { Loader2Icon } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useStoreWithEqualityFn } from 'zustand/traditional';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '../ui/dialog';
import { TxList } from './TxList';

export const TransactionDialogProvider = () => {
const { t } = useTranslation('tx');

const [isOpen, isReady] = useStoreWithEqualityFn(
txStore,
(state) => [state.isFetching || state.isReady, state.isReady] as const,
);

const onClose = (open: boolean) => {
if (!open) {
txStore.getState().reset();
}
};

const handleEscapes = (e: Event) => {
if (!isReady) {
txStore.getState().reset();
return;
}
e.preventDefault();
};

return (
<Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent
className={clsx(!isReady && 'w-64 h-64')}
onInteractOutside={handleEscapes}
onEscapeKeyDown={handleEscapes}
>
{isReady ? (
<TxList />
) : (
<>
<DialogHeader className="sr-only">
<DialogTitle>{t(($) => $.title)}</DialogTitle>
<DialogDescription>{t(($) => $.description)}</DialogDescription>
</DialogHeader>
<div className="flex flex-col justify-center items-center gap-4">
<Loader2Icon className="mr-2 animate-spin" size={48} />
<p className="text-sm">{t(($) => $.preparing)}</p>
</div>
</>
)}
</DialogContent>
</Dialog>
);
};
63 changes: 63 additions & 0 deletions apps/web-app/src/components/TransactionDialog/TransactionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { TRANSACTION_STATE, type SlayerTx } from '@/lib/transactions/store';
import { isTransactionRequest } from '@sovryn/slayer-sdk';
import {
CircleCheckBig,
CircleDashed,
CircleX,
ExternalLink,
Loader2Icon,
} from 'lucide-react';
import { useMemo, type FC } from 'react';
import { useChains } from 'wagmi';
import { LinkToExplorer } from '../LinkToExplorer/LinkToExplorer';

type TransactionItemProps = {
index: number;
item: SlayerTx;
};

export const TransactionItem: FC<TransactionItemProps> = ({ item, index }) => {
const isTx = isTransactionRequest(item);
const chains = useChains();
const chain = useMemo(() => {
if (isTx) {
const chainId = item.request.data.chain?.id;
return chains.find((c) => c.id === chainId);
}
return undefined;
}, [chains, isTx, item.request.data]);

return (
<div className="flex flex-row justify-start items-start gap-4 mb-3">
<div className="w-8 shrink-0 grow-0 text-center">
<div className="flex flex-col items-center gap-1">
{item.state === TRANSACTION_STATE.idle && <CircleDashed size={24} />}
{item.state === TRANSACTION_STATE.pending && (
<Loader2Icon className="animate-spin" size={24} />
)}
{item.state === TRANSACTION_STATE.success && (
<CircleCheckBig size={24} className="text-green-500" />
)}
{item.state === TRANSACTION_STATE.error && (
<CircleX size={24} className="text-red-500" />
)}
<div className="text-xs">#{index + 1}</div>
</div>
</div>
<div className="grow">
<p>{item.title}</p>
<p className="text-sm">{item.description}</p>
{isTx && chain && item.res?.transactionHash && (
<p className="text-sm flex flex-row justify-start gap-2 items-center mt-2">
<ExternalLink size={16} />
<LinkToExplorer chain={chain} txHash={item.res.transactionHash} />
</p>
)}

{item.error && (
<p className="mt-2 text-xs text-red-500">{item.error}</p>
)}
</div>
</div>
);
};
Loading