@xswap-link/sdk 0.10.6 → 0.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/main.yml +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/index.d.mts +39 -8
- package/dist/index.d.ts +39 -8
- package/dist/index.global.js +827 -440
- package/dist/index.js +9 -9
- package/dist/index.mjs +9 -9
- package/package.json +12 -1
- package/src/components/AllWalletsConfig/index.tsx +40 -0
- package/src/components/Swap/HistoryView/index.tsx +28 -5
- package/src/components/Swap/SwapView/ConfirmationView/TxOverview/index.tsx +73 -47
- package/src/components/Swap/SwapView/SwapButton/index.tsx +45 -10
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +123 -4
- package/src/components/Swap/SwapView/SwapPanel/WalletPanel/index.tsx +71 -0
- package/src/components/Swap/SwapView/SwapPanel/index.tsx +4 -0
- package/src/components/Swap/SwapView/WalletPicker/index.tsx +63 -28
- package/src/components/Swap/index.tsx +2 -2
- package/src/components/TxConfigForm/index.tsx +9 -34
- package/src/components/TxDataCard/TxDataCardUI/index.tsx +16 -10
- package/src/components/TxDataCard/index.tsx +1 -0
- package/src/components/index.ts +1 -0
- package/src/config/wagmiConfig.ts +1 -6
- package/src/constants/index.ts +14 -0
- package/src/context/HistoryProvider.tsx +121 -25
- package/src/context/SwapProvider.tsx +104 -48
- package/src/context/TransactionProvider.tsx +60 -1
- package/src/context/TxUIWrapper.tsx +187 -71
- package/src/context/WalletProvider.tsx +108 -0
- package/src/contracts/addresses.ts +4 -0
- package/src/contracts/idl/jupiter.ts +2879 -0
- package/src/models/Ecosystem.ts +1 -0
- package/src/models/Route.ts +14 -3
- package/src/models/SolanaTransaction.ts +38 -0
- package/src/models/SolanaWeb3Network.ts +5 -0
- package/src/models/TxUIWrapper.ts +8 -4
- package/src/models/index.ts +1 -0
- package/src/models/payloads/GetBalancesPayload.ts +2 -1
- package/src/models/payloads/GetSolanaRoutePayload.ts +8 -0
- package/src/models/payloads/index.ts +1 -0
- package/src/services/api.ts +21 -3
- package/src/services/solana/jupiter/extract-swap-data.ts +183 -0
- package/src/services/solana/jupiter/get-events.ts +37 -0
- package/src/services/solana/jupiter/instruction-parser.ts +217 -0
- package/src/services/solana/jupiter/utils.ts +37 -0
- package/src/utils/strings.ts +16 -0
- package/test/context/SwapProvider.test.tsx +6 -9
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CloseIcon, WalletConnectIcon } from "@src/assets/icons";
|
|
2
|
-
import { FC, useCallback
|
|
2
|
+
import { FC, useCallback } from "react";
|
|
3
3
|
import { Connector, useConnect } from "wagmi";
|
|
4
|
+
import { useWallet as useSolanaWallet } from "@solana/wallet-adapter-react";
|
|
5
|
+
import { Ecosystem } from "@src/models";
|
|
4
6
|
|
|
5
7
|
const ConnectorIcon: FC<{ connector: Connector; className?: string }> = ({
|
|
6
8
|
connector,
|
|
@@ -26,30 +28,41 @@ const ConnectorIcon: FC<{ connector: Connector; className?: string }> = ({
|
|
|
26
28
|
}
|
|
27
29
|
};
|
|
28
30
|
|
|
29
|
-
export const WalletPicker: FC<{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
export const WalletPicker: FC<{
|
|
32
|
+
ecosystem: Ecosystem;
|
|
33
|
+
className?: string;
|
|
34
|
+
onClose: () => void;
|
|
35
|
+
}> = ({ ecosystem, className, onClose }) => {
|
|
33
36
|
const { connectors, connectAsync } = useConnect();
|
|
37
|
+
const { wallets, select, connect } = useSolanaWallet();
|
|
34
38
|
|
|
35
|
-
const
|
|
36
|
-
return connectors.filter((connector) => {
|
|
37
|
-
const isInjected = connector.type === "injected";
|
|
38
|
-
const isInjectedGeneric = connector.name === "Injected";
|
|
39
|
-
const isWalletConnect = connector.type === "walletConnect";
|
|
40
|
-
|
|
41
|
-
return isWalletConnect || (isInjected && !isInjectedGeneric);
|
|
42
|
-
});
|
|
43
|
-
}, [connectors]);
|
|
44
|
-
|
|
45
|
-
const handleWalletClick = useCallback(
|
|
39
|
+
const handleEvmWalletClick = useCallback(
|
|
46
40
|
async (connector: Connector) => {
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
try {
|
|
42
|
+
await connectAsync({ connector });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error("Failed to connect EVM wallet:", error);
|
|
45
|
+
} finally {
|
|
46
|
+
onClose();
|
|
47
|
+
}
|
|
49
48
|
},
|
|
50
49
|
[connectAsync, onClose],
|
|
51
50
|
);
|
|
52
51
|
|
|
52
|
+
const handleConnectSolana = useCallback(
|
|
53
|
+
async (walletName) => {
|
|
54
|
+
try {
|
|
55
|
+
select(walletName);
|
|
56
|
+
await connect();
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error("Failed to connect Solana wallet:", error);
|
|
59
|
+
} finally {
|
|
60
|
+
onClose();
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
[connect, select, onClose],
|
|
64
|
+
);
|
|
65
|
+
|
|
53
66
|
return (
|
|
54
67
|
<div className={`flex flex-col h-full ${className || ""}`}>
|
|
55
68
|
<div className="flex justify-between">
|
|
@@ -63,16 +76,38 @@ export const WalletPicker: FC<{ className?: string; onClose: () => void }> = ({
|
|
|
63
76
|
</div>
|
|
64
77
|
<div className="horizontal-separator" />
|
|
65
78
|
<div className="h-full pt-4">
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
{ecosystem === Ecosystem.EVM && (
|
|
80
|
+
<>
|
|
81
|
+
{connectors.map((connector) => (
|
|
82
|
+
<button
|
|
83
|
+
key={connector.id}
|
|
84
|
+
onClick={() => handleEvmWalletClick(connector)}
|
|
85
|
+
className="flex items-center w-full gap-3 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-wallet-item"
|
|
86
|
+
>
|
|
87
|
+
<ConnectorIcon connector={connector} />
|
|
88
|
+
<div className="text-t_text_primary">{connector.name}</div>
|
|
89
|
+
</button>
|
|
90
|
+
))}
|
|
91
|
+
</>
|
|
92
|
+
)}
|
|
93
|
+
{ecosystem === Ecosystem.SOLANA && (
|
|
94
|
+
<>
|
|
95
|
+
{wallets.map((wallet) => (
|
|
96
|
+
<button
|
|
97
|
+
key={wallet.adapter.name}
|
|
98
|
+
onClick={() => handleConnectSolana(wallet.adapter.name)}
|
|
99
|
+
className="flex items-center w-full gap-3 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-wallet-item"
|
|
100
|
+
>
|
|
101
|
+
<img
|
|
102
|
+
src={wallet.adapter.icon}
|
|
103
|
+
alt={wallet.adapter.name}
|
|
104
|
+
className={`w-[34px] h-[34px] rounded-md ${className || ""}`}
|
|
105
|
+
/>
|
|
106
|
+
<div className="text-t_text_primary">{wallet.adapter.name}</div>
|
|
107
|
+
</button>
|
|
108
|
+
))}
|
|
109
|
+
</>
|
|
110
|
+
)}
|
|
76
111
|
</div>
|
|
77
112
|
</div>
|
|
78
113
|
);
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useSwapContext } from "@src/context";
|
|
2
|
-
import { Route } from "@src/models";
|
|
3
2
|
import { useMemo } from "react";
|
|
4
3
|
import { Header } from "./Header";
|
|
5
4
|
import { HistoryView } from "./HistoryView";
|
|
6
5
|
import { SettingsView } from "./SettingsView";
|
|
7
6
|
import { SwapView } from "./SwapView";
|
|
7
|
+
import { UnifiedRoute } from "@src/models";
|
|
8
8
|
|
|
9
9
|
type Props = {
|
|
10
|
-
onSubmit: (route:
|
|
10
|
+
onSubmit: (route: UnifiedRoute) => void;
|
|
11
11
|
onClose: () => void;
|
|
12
12
|
};
|
|
13
13
|
export const Swap = ({ onClose }: Props) => {
|
|
@@ -7,13 +7,13 @@ import {
|
|
|
7
7
|
TxUIWrapper,
|
|
8
8
|
useHistory,
|
|
9
9
|
} from "@src/context";
|
|
10
|
-
import { Chain, ModalIntegrationPayload,
|
|
11
|
-
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
10
|
+
import { Chain, ModalIntegrationPayload, UnifiedRoute } from "@src/models";
|
|
12
11
|
import { FC, MouseEvent, useEffect, useMemo } from "react";
|
|
13
12
|
import { ErrorBoundary } from "react-error-boundary";
|
|
14
|
-
import { useReconnect, WagmiProvider } from "wagmi";
|
|
15
13
|
import { changeHostVariableColor } from "../../../localTheme";
|
|
16
14
|
import { setApiUrl } from "@src/services/api";
|
|
15
|
+
import { WalletProvider } from "@src/context/WalletProvider";
|
|
16
|
+
import { AllWalletsConfig } from "../AllWalletsConfig";
|
|
17
17
|
|
|
18
18
|
export type TxConfigFormPayload = Omit<
|
|
19
19
|
ModalIntegrationPayload,
|
|
@@ -33,7 +33,7 @@ export type TxConfigFormPayload = Omit<
|
|
|
33
33
|
type TxConfigFormProps = TxConfigFormPayload &
|
|
34
34
|
(
|
|
35
35
|
| {
|
|
36
|
-
onSubmit: (route:
|
|
36
|
+
onSubmit: (route: UnifiedRoute) => void;
|
|
37
37
|
onClose: () => void;
|
|
38
38
|
overlay: true;
|
|
39
39
|
}
|
|
@@ -44,29 +44,6 @@ type TxConfigFormProps = TxConfigFormPayload &
|
|
|
44
44
|
}
|
|
45
45
|
);
|
|
46
46
|
|
|
47
|
-
const queryClient = new QueryClient();
|
|
48
|
-
|
|
49
|
-
const WagmiReload: FC = () => {
|
|
50
|
-
const { reconnect } = useReconnect();
|
|
51
|
-
|
|
52
|
-
useEffect(() => {
|
|
53
|
-
let count = 0;
|
|
54
|
-
const interval = setInterval(() => {
|
|
55
|
-
reconnect();
|
|
56
|
-
count++;
|
|
57
|
-
if (count >= 5) {
|
|
58
|
-
clearInterval(interval);
|
|
59
|
-
}
|
|
60
|
-
}, 1000);
|
|
61
|
-
|
|
62
|
-
return () => {
|
|
63
|
-
clearInterval(interval);
|
|
64
|
-
};
|
|
65
|
-
}, [reconnect]);
|
|
66
|
-
|
|
67
|
-
return <></>;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
47
|
const normalizeTxConfigFormProps = (props: TxConfigFormProps) => {
|
|
71
48
|
return {
|
|
72
49
|
...props,
|
|
@@ -165,9 +142,8 @@ export const TxConfigForm = (props: TxConfigFormProps) => {
|
|
|
165
142
|
<div>Something went wrong, please contact XSwap.</div>
|
|
166
143
|
)}
|
|
167
144
|
>
|
|
168
|
-
<
|
|
169
|
-
<
|
|
170
|
-
<WagmiReload />
|
|
145
|
+
<AllWalletsConfig wagmiConfig={wagmiConfig}>
|
|
146
|
+
<WalletProvider>
|
|
171
147
|
<HistoryProvider>
|
|
172
148
|
<PendingTransactionsEmitter
|
|
173
149
|
onPendingTransactionsChange={onPendingTransactionsChange}
|
|
@@ -198,7 +174,6 @@ export const TxConfigForm = (props: TxConfigFormProps) => {
|
|
|
198
174
|
highlightedDstTokens,
|
|
199
175
|
}}
|
|
200
176
|
supportedChains={supportedChains}
|
|
201
|
-
wagmiConfig={wagmiConfig}
|
|
202
177
|
overlay={overlay}
|
|
203
178
|
>
|
|
204
179
|
<TransactionProvider>
|
|
@@ -223,8 +198,8 @@ export const TxConfigForm = (props: TxConfigFormProps) => {
|
|
|
223
198
|
</TransactionProvider>
|
|
224
199
|
</SwapProvider>
|
|
225
200
|
</HistoryProvider>
|
|
226
|
-
</
|
|
227
|
-
</
|
|
201
|
+
</WalletProvider>
|
|
202
|
+
</AllWalletsConfig>
|
|
228
203
|
</ErrorBoundary>
|
|
229
204
|
);
|
|
230
205
|
};
|
|
@@ -248,7 +223,7 @@ export const openTxConfigForm = async ({
|
|
|
248
223
|
srcTokenLocked,
|
|
249
224
|
srcChainLocked,
|
|
250
225
|
override,
|
|
251
|
-
}: TxConfigFormPayload): Promise<
|
|
226
|
+
}: TxConfigFormPayload): Promise<UnifiedRoute> => {
|
|
252
227
|
try {
|
|
253
228
|
if (xpayRoot === null) {
|
|
254
229
|
throw new Error("XPay was incorrectly initialized");
|
|
@@ -32,16 +32,22 @@ export const TxDataCardUI = ({
|
|
|
32
32
|
|
|
33
33
|
const date = getDate(transaction.timestamp);
|
|
34
34
|
|
|
35
|
-
const transferredAmount =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
const transferredAmount =
|
|
36
|
+
transaction.sourceChainId === "mainnet-beta"
|
|
37
|
+
? transaction.amountWei
|
|
38
|
+
: weiToHumanReadable({
|
|
39
|
+
amount: transaction.amountWei,
|
|
40
|
+
decimals: srcToken?.decimals || 18,
|
|
41
|
+
precisionFractionalPlaces: 4,
|
|
42
|
+
});
|
|
43
|
+
const receivedAmount =
|
|
44
|
+
transaction.sourceChainId === "mainnet-beta"
|
|
45
|
+
? (transaction.tokenOutAmount as string)
|
|
46
|
+
: weiToHumanReadable({
|
|
47
|
+
amount: transaction?.tokenOutAmount || "0",
|
|
48
|
+
decimals: dstToken?.decimals || 18,
|
|
49
|
+
precisionFractionalPlaces: 4,
|
|
50
|
+
});
|
|
45
51
|
|
|
46
52
|
const srcChain = supportedChains.find(
|
|
47
53
|
(chain) => chain.chainId === transaction.sourceChainId,
|
package/src/components/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { Transport } from "viem";
|
|
|
4
4
|
import * as WagmiChains from "viem/chains";
|
|
5
5
|
import { mainnet } from "viem/chains";
|
|
6
6
|
import { Config, createConfig, createStorage, http } from "@wagmi/core";
|
|
7
|
-
import {
|
|
7
|
+
import { walletConnect } from "@wagmi/connectors";
|
|
8
8
|
|
|
9
9
|
const storage = createStorage({
|
|
10
10
|
key: "xpay.wagmi.store",
|
|
@@ -18,11 +18,6 @@ export const getWagmiConfig = (supportedChains: Chain[]): Config => {
|
|
|
18
18
|
chains,
|
|
19
19
|
transports,
|
|
20
20
|
connectors: [
|
|
21
|
-
injected(),
|
|
22
|
-
coinbaseWallet({
|
|
23
|
-
appLogoUrl: "https://xswap.link/favicon.ico",
|
|
24
|
-
appName: "XSwap",
|
|
25
|
-
}),
|
|
26
21
|
walletConnect({
|
|
27
22
|
projectId: "c392898b45ac587a280b5eb33e92aeb0",
|
|
28
23
|
showQrModal: true,
|
package/src/constants/index.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
1
2
|
import { Ecosystem } from "@src/models";
|
|
2
3
|
import { constants } from "ethers";
|
|
3
4
|
|
|
5
|
+
export const SOLANA_NATIVE_TOKEN_ADDRESS = new PublicKey(
|
|
6
|
+
"So11111111111111111111111111111111111111112",
|
|
7
|
+
);
|
|
8
|
+
export const SOLANA_TOKEN_PROGRAM_ID = new PublicKey(
|
|
9
|
+
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
|
|
10
|
+
);
|
|
11
|
+
export const METADATA_PROGRAM_ID =
|
|
12
|
+
"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s";
|
|
13
|
+
export const SOLANA_MAINNET_RPC_URL =
|
|
14
|
+
"https://orbital-thrilling-water.solana-mainnet.quiknode.pro/6b4a6d73d2cbe0590ce45bad3b375b183acef83f/";
|
|
15
|
+
export const JUPITER_V6_PROGRAM_ID = new PublicKey(
|
|
16
|
+
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4",
|
|
17
|
+
);
|
|
4
18
|
export const DEFAULT_ECOSYSTEM = Ecosystem.EVM;
|
|
5
19
|
export const NUMBER_INPUT_REGEX = /^[0-9]*[.,]?[0-9]*$/;
|
|
6
20
|
export const SLIPPAGE_PRESETS: number[] = [0.5, 1.5, 3.0];
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
1
2
|
import { CloseIcon } from "@src/assets/icons";
|
|
2
3
|
import { Button, SnackMessage } from "@src/components";
|
|
3
4
|
import {
|
|
4
5
|
CCIP_EXPLORER,
|
|
5
6
|
FALLBACK_CROSSCHAIN_ESTIMATION_TIME,
|
|
7
|
+
SOLANA_MAINNET_RPC_URL,
|
|
6
8
|
crosschainEstimationTimes,
|
|
7
9
|
} from "@src/constants";
|
|
10
|
+
import { useWallet } from "@src/context/WalletProvider";
|
|
8
11
|
import { useDebounce } from "@src/hooks";
|
|
9
12
|
import { Transaction, TransactionStatus } from "@src/models";
|
|
13
|
+
import { TransactionWithMeta } from "@src/models/SolanaTransaction";
|
|
10
14
|
import { getHistory } from "@src/services";
|
|
15
|
+
import { extractSolanaSwapData } from "@src/services/solana/jupiter/extract-swap-data";
|
|
16
|
+
import { isJupiterTransaction } from "@src/services/solana/jupiter/utils";
|
|
11
17
|
import { isServer } from "@src/utils";
|
|
12
18
|
import BigNumberJS from "bignumber.js";
|
|
13
19
|
import { useSnackbar } from "notistack";
|
|
@@ -21,7 +27,6 @@ import {
|
|
|
21
27
|
useMemo,
|
|
22
28
|
useState,
|
|
23
29
|
} from "react";
|
|
24
|
-
import { useAccount } from "wagmi";
|
|
25
30
|
|
|
26
31
|
export interface HistoryState {
|
|
27
32
|
history: Transaction[];
|
|
@@ -34,6 +39,8 @@ export interface HistoryState {
|
|
|
34
39
|
userAddress: string,
|
|
35
40
|
) => void;
|
|
36
41
|
historyLoadedOnce: boolean;
|
|
42
|
+
solanaHistory: Transaction[];
|
|
43
|
+
solanaHistoryLoadedOnce: boolean;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
const HistoryContext: Context<HistoryState> = createContext<HistoryState>(
|
|
@@ -63,11 +70,48 @@ const setLocalHistory = (data: LocalHistory) => {
|
|
|
63
70
|
}
|
|
64
71
|
};
|
|
65
72
|
|
|
73
|
+
async function processSolanaTransactions(
|
|
74
|
+
transactions: (TransactionWithMeta | null)[],
|
|
75
|
+
): Promise<Transaction[]> {
|
|
76
|
+
const results: Transaction[] = [];
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < transactions.length; i++) {
|
|
79
|
+
try {
|
|
80
|
+
const tx = transactions[i];
|
|
81
|
+
if (!tx) continue;
|
|
82
|
+
|
|
83
|
+
const signature = tx.transaction.signatures[0];
|
|
84
|
+
|
|
85
|
+
if (!signature) continue;
|
|
86
|
+
|
|
87
|
+
const connection = new Connection(SOLANA_MAINNET_RPC_URL, "confirmed");
|
|
88
|
+
|
|
89
|
+
const swapDetails = await extractSolanaSwapData(
|
|
90
|
+
signature,
|
|
91
|
+
connection,
|
|
92
|
+
tx,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (swapDetails) {
|
|
96
|
+
results.push(swapDetails);
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`Error processing transaction:`, error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
|
|
66
106
|
export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
67
|
-
const {
|
|
107
|
+
const { evm, solana } = useWallet();
|
|
108
|
+
const { address: address } = evm;
|
|
109
|
+
const { address: solanaAddress } = solana;
|
|
68
110
|
|
|
111
|
+
const [solanaHistory, setSolanaHistory] = useState<Transaction[]>([]);
|
|
69
112
|
const [history, setHistory] = useState<Transaction[]>([]);
|
|
70
113
|
const [historyLoadedOnce, setHistoryLoadedOnce] = useState(false);
|
|
114
|
+
const [solanaHistoryLoadedOnce, setSolanaHistoryLoadedOnce] = useState(false);
|
|
71
115
|
|
|
72
116
|
const { enqueueSnackbar, closeSnackbar } = useSnackbar();
|
|
73
117
|
const { debounce: getHistoryDebounce } = useDebounce({
|
|
@@ -82,16 +126,70 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
82
126
|
}
|
|
83
127
|
}, []);
|
|
84
128
|
|
|
129
|
+
const fetchLastSignatures = async (senderAddress: string, limit = 50) => {
|
|
130
|
+
const connection = new Connection(SOLANA_MAINNET_RPC_URL, "confirmed");
|
|
131
|
+
const userPubkey = new PublicKey(senderAddress);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const signatures = await connection.getSignaturesForAddress(userPubkey, {
|
|
135
|
+
limit,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const transactions = await connection.getParsedTransactions(
|
|
139
|
+
signatures.map((s) => s.signature),
|
|
140
|
+
{
|
|
141
|
+
maxSupportedTransactionVersion: 0,
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const jupiterTransactions = transactions
|
|
146
|
+
.filter((val) => !!val)
|
|
147
|
+
.filter((tx) => isJupiterTransaction(tx));
|
|
148
|
+
|
|
149
|
+
const swapDetails = await processSolanaTransactions(
|
|
150
|
+
jupiterTransactions as TransactionWithMeta[],
|
|
151
|
+
);
|
|
152
|
+
return swapDetails;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error("Error fetching transactions:", error);
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const fetchSolanaHistory = async () => {
|
|
160
|
+
if (!solanaAddress) {
|
|
161
|
+
setSolanaHistoryLoadedOnce(true);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
const lastSignatures = await fetchLastSignatures(solanaAddress);
|
|
167
|
+
setSolanaHistory(lastSignatures);
|
|
168
|
+
} finally {
|
|
169
|
+
setSolanaHistoryLoadedOnce(true);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
if (solanaAddress) {
|
|
175
|
+
fetchSolanaHistory();
|
|
176
|
+
} else {
|
|
177
|
+
setSolanaHistory([]);
|
|
178
|
+
setSolanaHistoryLoadedOnce(true);
|
|
179
|
+
}
|
|
180
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
181
|
+
}, [solanaAddress]);
|
|
182
|
+
|
|
85
183
|
const fetchHistory = useCallback(async () => {
|
|
86
184
|
try {
|
|
87
185
|
if (!address) {
|
|
88
186
|
return;
|
|
89
187
|
}
|
|
90
188
|
|
|
189
|
+
const fetchedTransactions: Transaction[] = [];
|
|
91
190
|
const fetchedHistory = await getHistory({
|
|
92
191
|
walletAddress: address,
|
|
93
192
|
});
|
|
94
|
-
const fetchedTransactions: Transaction[] = [];
|
|
95
193
|
|
|
96
194
|
for (const entry of fetchedHistory.history) {
|
|
97
195
|
if (!entry.source) {
|
|
@@ -158,15 +256,16 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
158
256
|
for (const [walletAddress, transactions] of Object.entries(
|
|
159
257
|
localHistory,
|
|
160
258
|
)) {
|
|
161
|
-
if (
|
|
162
|
-
walletAddress.toLowerCase().toLowerCase() !== address.toLowerCase()
|
|
163
|
-
) {
|
|
259
|
+
if (walletAddress.toLowerCase() !== address.toLowerCase()) {
|
|
164
260
|
continue;
|
|
165
261
|
}
|
|
166
262
|
const newLocalHistoryForWallet: Transaction[] = [];
|
|
167
263
|
|
|
168
264
|
for (const transaction of transactions) {
|
|
169
|
-
|
|
265
|
+
const txHash = transaction.hash;
|
|
266
|
+
const apiTransaction = txHashToTxFromAPI[txHash];
|
|
267
|
+
|
|
268
|
+
if (!apiTransaction) {
|
|
170
269
|
// localStorage has fresher data.
|
|
171
270
|
fetchedTransactions.push(transaction);
|
|
172
271
|
newLocalHistoryForWallet.push(transaction);
|
|
@@ -174,20 +273,17 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
174
273
|
}
|
|
175
274
|
|
|
176
275
|
if (
|
|
177
|
-
|
|
276
|
+
apiTransaction &&
|
|
178
277
|
transaction.status === "DONE" &&
|
|
179
|
-
|
|
278
|
+
apiTransaction?.status === "IN_PROGRESS"
|
|
180
279
|
) {
|
|
181
280
|
// If we know on the frontend that the tx is done we should update backend result
|
|
182
|
-
|
|
183
|
-
txHashToTxFromAPI[transaction.hash].status = "DONE";
|
|
281
|
+
apiTransaction.status = "DONE";
|
|
184
282
|
}
|
|
185
283
|
|
|
186
284
|
if (
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// @ts-ignore
|
|
190
|
-
txHashToTxFromAPI[transaction.hash].status === "REVERTED"
|
|
285
|
+
apiTransaction.status === "DONE" ||
|
|
286
|
+
apiTransaction.status === "REVERTED"
|
|
191
287
|
) {
|
|
192
288
|
// API has the same or newer data.
|
|
193
289
|
continue;
|
|
@@ -212,7 +308,7 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
212
308
|
} finally {
|
|
213
309
|
setHistoryLoadedOnce(true);
|
|
214
310
|
}
|
|
215
|
-
}, [address,
|
|
311
|
+
}, [address, enqueueSnackbar]);
|
|
216
312
|
|
|
217
313
|
useEffect(() => {
|
|
218
314
|
if (!historyLoadedOnce) {
|
|
@@ -222,13 +318,7 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
222
318
|
getHistoryDebounce(fetchHistory);
|
|
223
319
|
}, 60000);
|
|
224
320
|
return () => clearInterval(timer);
|
|
225
|
-
}, [
|
|
226
|
-
address,
|
|
227
|
-
fetchHistory,
|
|
228
|
-
getHistory,
|
|
229
|
-
getHistoryDebounce,
|
|
230
|
-
historyLoadedOnce,
|
|
231
|
-
]);
|
|
321
|
+
}, [address, fetchHistory, getHistoryDebounce, historyLoadedOnce]);
|
|
232
322
|
|
|
233
323
|
const addTransactionToLocalHistory = useCallback(
|
|
234
324
|
(transaction: Transaction, userAddress: string) => {
|
|
@@ -281,7 +371,7 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
281
371
|
{ persist: false, key: messageId },
|
|
282
372
|
);
|
|
283
373
|
},
|
|
284
|
-
[enqueueSnackbar],
|
|
374
|
+
[closeSnackbar, enqueueSnackbar],
|
|
285
375
|
);
|
|
286
376
|
|
|
287
377
|
const updateTransactionInLocalHistory = useCallback(
|
|
@@ -319,8 +409,10 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
319
409
|
|
|
320
410
|
useEffect(() => {
|
|
321
411
|
setHistoryLoadedOnce(false);
|
|
412
|
+
setSolanaHistoryLoadedOnce(false);
|
|
322
413
|
setHistory([]);
|
|
323
|
-
|
|
414
|
+
setSolanaHistory([]);
|
|
415
|
+
}, [address, solanaAddress]);
|
|
324
416
|
|
|
325
417
|
const state = useMemo<HistoryState>(
|
|
326
418
|
() => ({
|
|
@@ -328,12 +420,16 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
|
|
|
328
420
|
addTransactionToLocalHistory,
|
|
329
421
|
updateTransactionInLocalHistory,
|
|
330
422
|
historyLoadedOnce,
|
|
423
|
+
solanaHistory,
|
|
424
|
+
solanaHistoryLoadedOnce,
|
|
331
425
|
}),
|
|
332
426
|
[
|
|
333
427
|
history,
|
|
334
428
|
addTransactionToLocalHistory,
|
|
335
429
|
updateTransactionInLocalHistory,
|
|
336
430
|
historyLoadedOnce,
|
|
431
|
+
solanaHistory,
|
|
432
|
+
solanaHistoryLoadedOnce,
|
|
337
433
|
],
|
|
338
434
|
);
|
|
339
435
|
|