@deserialize/multi-vm-wallet 1.2.10 → 1.2.21

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.
Files changed (47) hide show
  1. package/dist/IChainWallet.d.ts +5 -1
  2. package/dist/IChainWallet.js.map +1 -1
  3. package/dist/constant.d.ts +16 -0
  4. package/dist/constant.js +19 -3
  5. package/dist/constant.js.map +1 -1
  6. package/dist/evm/evm.d.ts +6 -1
  7. package/dist/evm/evm.js +36 -45
  8. package/dist/evm/evm.js.map +1 -1
  9. package/dist/evm/transactionParsing.d.ts +3687 -0
  10. package/dist/evm/transactionParsing.js +441 -0
  11. package/dist/evm/transactionParsing.js.map +1 -0
  12. package/dist/evm/utils.d.ts +2 -9
  13. package/dist/evm/utils.js +17 -16
  14. package/dist/evm/utils.js.map +1 -1
  15. package/dist/helpers/index.d.ts +4 -0
  16. package/dist/helpers/index.js +13 -0
  17. package/dist/helpers/index.js.map +1 -0
  18. package/dist/svm/constant.d.ts +15 -0
  19. package/dist/svm/constant.js +25 -0
  20. package/dist/svm/constant.js.map +1 -0
  21. package/dist/svm/svm.d.ts +5 -2
  22. package/dist/svm/svm.js +10 -0
  23. package/dist/svm/svm.js.map +1 -1
  24. package/dist/svm/transactionParsing.d.ts +28 -0
  25. package/dist/svm/transactionParsing.js +207 -0
  26. package/dist/svm/transactionParsing.js.map +1 -0
  27. package/dist/svm/utils.d.ts +4 -3
  28. package/dist/svm/utils.js +82 -9
  29. package/dist/svm/utils.js.map +1 -1
  30. package/dist/test.d.ts +1 -1
  31. package/dist/test.js +47 -9
  32. package/dist/test.js.map +1 -1
  33. package/dist/types.d.ts +5 -1
  34. package/dist/types.js.map +1 -1
  35. package/package.json +4 -2
  36. package/utils/IChainWallet.ts +6 -2
  37. package/utils/constant.ts +22 -4
  38. package/utils/evm/evm.ts +53 -48
  39. package/utils/evm/transactionParsing.ts +639 -0
  40. package/utils/evm/utils.ts +26 -25
  41. package/utils/helpers/index.ts +11 -0
  42. package/utils/svm/constant.ts +29 -0
  43. package/utils/svm/svm.ts +14 -2
  44. package/utils/svm/transactionParsing.ts +294 -0
  45. package/utils/svm/utils.ts +104 -13
  46. package/utils/test.ts +56 -6
  47. package/utils/types.ts +6 -1
@@ -1,6 +1,7 @@
1
- import { Balance, TokenInfo } from '../types'
1
+ import { Balance, ChainWalletConfig, SUPPORTED_VM, UserTokenBalance, TokenInfo } from '../types'
2
2
  import { JsonRpcProvider, Contract, Wallet, TransactionRequest, TransactionResponse, TransactionReceipt, parseUnits, formatUnits } from 'ethers'
3
3
  import BN from 'bn.js'
4
+ import { HelperAPI } from '../helpers';
4
5
 
5
6
  const KYBER_BASE_URL = 'https://aggregator-api.kyberswap.com';
6
7
 
@@ -23,7 +24,7 @@ interface TransactionResult {
23
24
  effectiveGasPrice?: bigint
24
25
  blockNumber?: number
25
26
  confirmations: number
26
-
27
+
27
28
  }
28
29
 
29
30
  export interface SwapParams {
@@ -636,6 +637,22 @@ export const safeApprove = async (
636
637
  }
637
638
  }
638
639
 
640
+ export const discoverTokens = async (wallet: string, chain: ChainWalletConfig): Promise<UserTokenBalance<string>[]> => {
641
+ const balances = await HelperAPI.getUserToken(wallet, chain.vmType ?? "EVM", chain.chainId)
642
+
643
+
644
+ const formatBalances: UserTokenBalance<string>[] = balances.data.map((token: any) => {
645
+ return {
646
+ address: token.contractAddress,
647
+ name: token.name,
648
+ symbol: token.symbol,
649
+ decimals: token.decimals,
650
+ balance: token.balance,
651
+ owner: wallet
652
+ }
653
+ })
654
+ return formatBalances
655
+ }
639
656
 
640
657
  //swaps
641
658
 
@@ -769,9 +786,9 @@ export async function performSwap(params: {
769
786
  amountIn: params.amountIn,
770
787
  chainId: params.chainId
771
788
  });
772
-
789
+
773
790
  console.log('Fetching best swap route across all DEXs...');
774
-
791
+
775
792
  const routeResponse = await getKyberSwapRoute({
776
793
  chainId: params.chainId,
777
794
  tokenIn: params.tokenIn,
@@ -792,7 +809,7 @@ export async function performSwap(params: {
792
809
  }
793
810
 
794
811
  const { routeSummary, routerAddress } = routeResponse.data;
795
-
812
+
796
813
  // Debug: Log what we actually received
797
814
  console.log('routeSummary keys:', Object.keys(routeSummary));
798
815
  console.log('routeSummary.swaps exists:', !!routeSummary.swaps);
@@ -808,13 +825,13 @@ export async function performSwap(params: {
808
825
  // Only try to access swaps if it exists
809
826
  swapsCount: routeSummary.swaps ? routeSummary.swaps.length : 0,
810
827
  // Only extract exchange names if swaps exists and has the expected structure
811
- dexSources: routeSummary.swaps && Array.isArray(routeSummary.swaps)
828
+ dexSources: routeSummary.swaps && Array.isArray(routeSummary.swaps)
812
829
  ? routeSummary.swaps.map(swap => swap?.exchange || 'unknown').filter(Boolean)
813
830
  : ['unknown']
814
831
  });
815
832
 
816
833
  console.log('Building executable transaction...');
817
-
834
+
818
835
  const buildResponse = await buildKyberSwapTransaction(
819
836
  params.chainId,
820
837
  routeSummary,
@@ -852,7 +869,7 @@ export async function performSwap(params: {
852
869
 
853
870
  } catch (error) {
854
871
  console.error('❌ KyberSwap aggregation failed:', error);
855
-
872
+
856
873
  // More detailed error logging
857
874
  if (error instanceof Error) {
858
875
  console.error('Error details:', {
@@ -861,7 +878,7 @@ export async function performSwap(params: {
861
878
  name: error.name
862
879
  });
863
880
  }
864
-
881
+
865
882
  throw new Error(`Swap preparation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
866
883
  }
867
884
  }
@@ -915,22 +932,6 @@ export function prepareSwapParams(
915
932
  };
916
933
  }
917
934
 
918
- /**
919
- * Normalize token address for Debonk
920
- * Converts 'native' or variations to the standard 0xEeeee... address
921
- */
922
- // export function normalizeTokenAddressForDebonk(tokenAddress: string): string {
923
- // if (tokenAddress === 'native' ||
924
- // tokenAddress.toLowerCase() === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee') {
925
- // return getNativeTokenAddress();
926
- // }
927
- // return tokenAddress;
928
- // }
929
-
930
- /**
931
- * Convert slippage from basis points to percentage for Debonk
932
- * Input: 50 (0.5% in bps) -> Output: 0.5 (percentage)
933
- */
934
935
  export function convertSlippageForDebonk(slippageBps: number): number {
935
936
  return slippageBps / 100;
936
937
  }
@@ -0,0 +1,11 @@
1
+ import { vmTypes } from "../types";
2
+
3
+ const BASE_URL = "https://helper.decane.app"
4
+
5
+ export class HelperAPI {
6
+ static async getUserToken(wallet: string, vm: vmTypes, chainId: number) {
7
+ const res = await fetch("" + BASE_URL + `/${vm}/tokens/discover?wallet=${wallet}&chainId=${chainId}`);
8
+ const data = await res.json();
9
+ return data;
10
+ }
11
+ }
@@ -0,0 +1,29 @@
1
+ const SVM_NETWORKS_NAME = {
2
+ SOLANA_MAINNET: "SOLANA_MAINNET",
3
+ SOLANA_TESTNET: "SOLANA_TESTNET",
4
+ ECLIPSE_MAINNET: "ECLIPSE_MAINNET"
5
+ } as const
6
+
7
+
8
+
9
+
10
+ export const SVM_CHAIN_DETAILS: { [key: string]: SVMNetwork } = {
11
+ "123456789": SVM_NETWORKS_NAME.SOLANA_MAINNET,
12
+ "123456790": SVM_NETWORKS_NAME.SOLANA_TESTNET,
13
+ "123456791": SVM_NETWORKS_NAME.ECLIPSE_MAINNET
14
+ }
15
+
16
+ export type SVMNetwork = typeof SVM_NETWORKS_NAME[keyof typeof SVM_NETWORKS_NAME];
17
+
18
+ export const SVM_CHAIN_CONFIG: { [key in SVMNetwork]: { rpcUrl: string } } = {
19
+ [SVM_NETWORKS_NAME.SOLANA_MAINNET]: {
20
+ rpcUrl: "https://solana-mainnet.g.alchemy.com/v2/vB5mKztdJeFdz9RkW99Qf"
21
+ },
22
+ [SVM_NETWORKS_NAME.SOLANA_TESTNET]: {
23
+ rpcUrl: "https://solana-testnet.g.alchemy.com/v2/vB5mKztdJeFdz9RkW99Qf"
24
+ },
25
+ [SVM_NETWORKS_NAME.ECLIPSE_MAINNET]: {
26
+ rpcUrl: "https://mainnetbeta-rpc.eclipse.xyz"
27
+ }
28
+ }
29
+
package/utils/svm/svm.ts CHANGED
@@ -2,7 +2,7 @@ import { Connection, Keypair, PublicKey } from "@solana/web3.js";
2
2
  import { SVMDeriveChildPrivateKey } from "../walletBip32";
3
3
  import { VM } from "../vm";
4
4
  import { ChainWallet } from "../IChainWallet";
5
- import { Balance, ChainWalletConfig, TokenInfo, TransactionResult } from "../types";
5
+ import { Balance, ChainWalletConfig, UserTokenBalance, TokenInfo, TransactionResult } from "../types";
6
6
  import {
7
7
  getSvmNativeBalance,
8
8
  getTokenBalance,
@@ -15,11 +15,14 @@ import {
15
15
  uiAmountToBaseUnits,
16
16
  validateJupiterTokens,
17
17
  JupiterQuoteResponse,
18
- getTokenInfo
18
+ getTokenInfo,
19
+ discoverTokens
19
20
  } from "./utils";
20
21
  import BN from "bn.js";
21
22
  import nacl from "tweetnacl";
22
23
  import base58 from "bs58";
24
+ import { getSVMTransactionHistory, SVMTransactionHistoryItem } from "./transactionParsing";
25
+
23
26
 
24
27
  export class SVMVM extends VM<PublicKey, Keypair, Connection> {
25
28
  getTokenInfo = getTokenInfo
@@ -92,6 +95,11 @@ export class SVMChainWallet extends ChainWallet<PublicKey, Keypair, Connection>
92
95
  // Implement token balance retrieval logic here
93
96
  return await SVMVM.getTokenBalance(this.address, (tokenAddress), this.connection!);
94
97
  }
98
+ async discoverToken(): Promise<UserTokenBalance<PublicKey>[]> {
99
+ // Implement token discovery logic here
100
+ const tokens = await discoverTokens(this.address, this.connection!)
101
+ return tokens
102
+ }
95
103
 
96
104
  async transferNative(to: PublicKey, amount: number): Promise<TransactionResult> {
97
105
  // Implement native transfer logic here
@@ -107,6 +115,10 @@ export class SVMChainWallet extends ChainWallet<PublicKey, Keypair, Connection>
107
115
  return { success: true, hash }; // Placeholder
108
116
  }
109
117
 
118
+ async getTransactionHistory(): Promise<SVMTransactionHistoryItem[]> {
119
+ const history = await getSVMTransactionHistory(this.connection!, this.address);
120
+ return history;
121
+ }
110
122
 
111
123
  async swap(fromToken: TokenInfo, toToken: PublicKey, amount: number, slippage: number = 50): Promise<TransactionResult> {
112
124
  try {
@@ -0,0 +1,294 @@
1
+ import { Connection, PublicKey, ParsedTransactionWithMeta, ConfirmedSignatureInfo, TokenBalance } from '@solana/web3.js';
2
+ import { TRANSACTION_TYPE, TransactionType } from '../constant';
3
+
4
+ export interface SVMTransactionHistoryItem {
5
+ hash: string;
6
+ timestamp: number | null | undefined;
7
+ status: 'success' | 'failed' | 'pending';
8
+ fee: number;
9
+ type: TransactionType;
10
+ from: string;
11
+ to?: string;
12
+
13
+
14
+
15
+ slot: number;
16
+ amount?: number;
17
+ token?: string;
18
+ memo?: string;
19
+ }
20
+
21
+ export interface TransactionHistoryOptions {
22
+ limit?: number;
23
+ before?: string;
24
+ until?: string;
25
+ }
26
+
27
+ /**
28
+ * Fetches and parses transaction history for a Solana wallet address
29
+ * @param connection - Solana RPC connection
30
+ * @param walletAddress - Public key of the wallet (string or PublicKey)
31
+ * @param options - Optional parameters for pagination and filtering
32
+ * @returns Array of parsed transaction history items
33
+ */
34
+ export async function getSVMTransactionHistory(
35
+ connection: Connection,
36
+ walletAddress: PublicKey,
37
+ options: TransactionHistoryOptions = {}
38
+ ): Promise<SVMTransactionHistoryItem[]> {
39
+ const { limit = 50, before, until } = options;
40
+
41
+ const publicKey = typeof walletAddress === 'string'
42
+ ? new PublicKey(walletAddress)
43
+ : walletAddress;
44
+
45
+ try {
46
+ // Fetch signature info
47
+ const signatures = await connection.getSignaturesForAddress(publicKey, {
48
+ limit,
49
+ before,
50
+ until,
51
+ });
52
+
53
+ console.log(`Found ${signatures.length} transactions`);
54
+
55
+ // Fetch and parse transactions in batches to avoid rate limits
56
+ const transactions = await fetchTransactionsInBatches(
57
+ connection,
58
+ signatures,
59
+ 5 // batch size
60
+ );
61
+
62
+ // Parse transactions into a user-friendly format
63
+ const history: SVMTransactionHistoryItem[] = [];
64
+
65
+ for (let i = 0; i < transactions.length; i++) {
66
+ const tx = transactions[i];
67
+ const sigInfo = signatures[i];
68
+
69
+ if (!tx) {
70
+ // Transaction might be null if it's not available
71
+ history.push({
72
+ hash: sigInfo.signature,
73
+ timestamp: sigInfo.blockTime,
74
+ slot: sigInfo.slot,
75
+ status: sigInfo.err ? 'failed' : 'success',
76
+ fee: 0,
77
+ type: TRANSACTION_TYPE.UNKNOWN,
78
+ from: walletAddress.toBase58()
79
+ });
80
+ continue;
81
+ }
82
+
83
+ const parsed = parseTransaction(tx, publicKey.toBase58());
84
+ history.push({
85
+ hash: sigInfo.signature,
86
+ timestamp: sigInfo.blockTime,
87
+ slot: sigInfo.slot,
88
+ status: tx.meta?.err ? 'failed' : 'success',
89
+ fee: parsed.fee ?? 0,
90
+ type: parsed.type ?? TRANSACTION_TYPE.UNKNOWN,
91
+ from: parsed.from ?? walletAddress.toBase58(),
92
+ ...parsed,
93
+ });
94
+ }
95
+
96
+ return history;
97
+ } catch (error) {
98
+ console.error('Error fetching transaction history:', error);
99
+ throw error;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Fetches transactions in batches to avoid overwhelming the RPC
105
+ */
106
+ async function fetchTransactionsInBatches(
107
+ connection: Connection,
108
+ signatures: ConfirmedSignatureInfo[],
109
+ batchSize: number
110
+ ): Promise<(ParsedTransactionWithMeta | null)[]> {
111
+ const transactions: (ParsedTransactionWithMeta | null)[] = [];
112
+
113
+ for (let i = 0; i < signatures.length; i += batchSize) {
114
+ const batch = signatures.slice(i, i + batchSize);
115
+ const batchPromises = batch.map(sig =>
116
+ connection.getParsedTransaction(sig.signature, {
117
+ maxSupportedTransactionVersion: 0,
118
+ })
119
+ );
120
+
121
+ const batchResults = await Promise.all(batchPromises);
122
+ transactions.push(...batchResults);
123
+
124
+ // Small delay to avoid rate limiting
125
+ if (i + batchSize < signatures.length) {
126
+ await new Promise(resolve => setTimeout(resolve, 100));
127
+ }
128
+ }
129
+
130
+ return transactions;
131
+ }
132
+
133
+ /**
134
+ * Parses a transaction into a simplified format
135
+ */
136
+ function parseTransaction(
137
+ tx: ParsedTransactionWithMeta,
138
+ walletAddress: string
139
+ ): Partial<SVMTransactionHistoryItem> {
140
+ const fee = tx.meta?.fee || 0;
141
+
142
+ // Try to determine transaction type and extract relevant info
143
+ const instructions = tx.transaction.message.instructions;
144
+
145
+ // Check for token transfers
146
+ if (tx.meta?.preTokenBalances && tx.meta?.postTokenBalances) {
147
+ // console.log('tx.meta: ', tx.meta);
148
+ const tokenTransfer = findTokenTransfer(
149
+ tx.meta.preTokenBalances,
150
+ tx.meta.postTokenBalances,
151
+ walletAddress
152
+ );
153
+
154
+ if (tokenTransfer) {
155
+ return {
156
+ fee,
157
+ type: TRANSACTION_TYPE.TOKEN_TRANSFER,
158
+ from: tokenTransfer.from,
159
+ to: tokenTransfer.to,
160
+ amount: tokenTransfer.amount,
161
+ token: tokenTransfer.mint,
162
+ };
163
+ }
164
+ }
165
+
166
+ // Check for SOL transfers
167
+ if (tx.meta?.preBalances && tx.meta?.postBalances) {
168
+ const solTransfer = findSolTransfer(
169
+ tx.meta.preBalances,
170
+ tx.meta.postBalances,
171
+ tx.transaction.message.accountKeys,
172
+ walletAddress
173
+ );
174
+
175
+ if (solTransfer) {
176
+ return {
177
+ fee,
178
+ type: TRANSACTION_TYPE.NATIVE_TRANSFER,
179
+ from: solTransfer.from,
180
+ to: solTransfer.to,
181
+ amount: solTransfer.amount,
182
+ token: 'SOL',
183
+ };
184
+ }
185
+ }
186
+
187
+ // Check for memo
188
+ const memo = extractMemo(instructions);
189
+
190
+ // Determine general type based on instructions
191
+ const type = determineTransactionType(instructions);
192
+
193
+ return {
194
+ fee,
195
+ type,
196
+ memo,
197
+ };
198
+ }
199
+
200
+ /**
201
+ * Finds token transfers in the transaction
202
+ */
203
+ function findTokenTransfer(
204
+ preBalances: TokenBalance[],
205
+ postBalances: TokenBalance[],
206
+ walletAddress: string
207
+ ): { from: string; to: string; amount: number; mint: string } | null {
208
+ for (let i = 0; i < postBalances.length; i++) {
209
+ const post = postBalances[i]
210
+ const pre = preBalances.find(
211
+ p => p.accountIndex === post.accountIndex
212
+ );
213
+
214
+ if (!pre) continue;
215
+
216
+ const preAmount = parseFloat(pre.uiTokenAmount.uiAmountString || "0");
217
+ const postAmount = parseFloat(post.uiTokenAmount.uiAmountString || "0");
218
+ const diff = postAmount - preAmount;
219
+ const from = diff < 0 ? post.owner ?? "unknown" : postBalances[i + 1]?.owner ?? "unknown";
220
+
221
+ let to = diff > 0 ? post.owner ?? "unknown" : postBalances[i + 1]?.owner ?? "unknown";
222
+ if (to === "unknown") {
223
+ to = diff > 0 ? post.owner ?? "unknown" : postBalances[i - 1]?.owner ?? "unknown"
224
+ }
225
+
226
+ if (from && to) {
227
+
228
+ if (Math.abs(diff) > 0) {
229
+ return {
230
+ from,
231
+ to,
232
+ amount: Math.abs(diff),
233
+ mint: post.mint,
234
+ };
235
+ }
236
+ }
237
+ }
238
+
239
+ return null;
240
+ }
241
+
242
+ /**
243
+ * Finds SOL transfers in the transaction
244
+ */
245
+ function findSolTransfer(
246
+ preBalances: number[],
247
+ postBalances: number[],
248
+ accountKeys: any[],
249
+ walletAddress: string
250
+ ): { from: string; to: string; amount: number } | null {
251
+ for (let i = 0; i < preBalances.length; i++) {
252
+ const diff = postBalances[i] - preBalances[i];
253
+ const account = accountKeys[i];
254
+ const accountPubkey = typeof account === 'string' ? account : account.pubkey.toBase58();
255
+
256
+ if (Math.abs(diff) > 5000 && accountPubkey === walletAddress) { // Ignore fee-only changes
257
+ return {
258
+ from: diff < 0 ? accountPubkey : 'unknown',
259
+ to: diff > 0 ? accountPubkey : 'unknown',
260
+ amount: Math.abs(diff) / 1e9, // Convert lamports to SOL
261
+ };
262
+ }
263
+ }
264
+
265
+ return null;
266
+ }
267
+
268
+ /**
269
+ * Extracts memo from transaction instructions
270
+ */
271
+ function extractMemo(instructions: any[]): string | undefined {
272
+ for (const instruction of instructions) {
273
+ if (instruction.program === 'spl-memo' || instruction.programId?.toBase58() === 'MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr') {
274
+ return instruction.parsed || instruction.data;
275
+ }
276
+ }
277
+ return undefined;
278
+ }
279
+
280
+ /**
281
+ * Determines the general type of transaction
282
+ */
283
+ function determineTransactionType(instructions: any[]): TransactionType {
284
+ if (instructions.length === 0) return TRANSACTION_TYPE.UNKNOWN;
285
+
286
+ const programs = instructions.map(i => i.program || i.programId?.toBase58());
287
+
288
+ if (programs.includes('spl-token')) return TRANSACTION_TYPE.TOKEN_INTERACTIONS;
289
+ if (programs.includes('system')) return TRANSACTION_TYPE.SYSTEM;
290
+ if (programs.some(p => p?.includes('Swap'))) return TRANSACTION_TYPE.SWAP;
291
+ if (programs.some(p => p?.includes('Stake'))) return TRANSACTION_TYPE.STAKING;
292
+
293
+ return TRANSACTION_TYPE.PROGRAM_INTERACTION;
294
+ }
@@ -2,9 +2,10 @@
2
2
 
3
3
  import { Account, createAssociatedTokenAccountIdempotentInstruction, createTransferCheckedInstruction, getAccount, getAssociatedTokenAddress, getAssociatedTokenAddressSync, getMint, Mint, NATIVE_MINT, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from "@solana/spl-token";
4
4
  import { Connection, Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
5
- import { TokenInfo } from "../types";
5
+ import { ChainWalletConfig, UserTokenBalance, TokenInfo } from "../types";
6
6
  import { transactionSenderAndConfirmationWaiter } from "./transactionSender";
7
7
  import { BN } from "bn.js";
8
+ import { Metaplex } from "@metaplex-foundation/js";
8
9
 
9
10
  const JUPITER_BASE_URL = 'https://lite-api.jup.ag';
10
11
 
@@ -270,9 +271,25 @@ export const getTransferNativeTransaction = async (from: Keypair, to: PublicKey,
270
271
  console.log('getTransferNativeTransaction: Completed');
271
272
  return transaction;
272
273
  }
274
+ const getMetaTokenMetaplexData = (mintAddress: PublicKey, connection: Connection) => {
275
+ const metaplex = Metaplex.make(connection);
276
+ return metaplex.nfts().findByMint({ mintAddress: mintAddress });
277
+ }
273
278
 
274
- export const getTokenInfo = async (tokenAddress: PublicKey, connection: Connection): Promise<TokenInfo> => {
279
+ export const getTokenInfo = async (tokenAddress: PublicKey, connection: Connection, programId?: PublicKey): Promise<TokenInfo> => {
275
280
  let mint: Mint
281
+
282
+
283
+ const metaplexData = await getMetaTokenMetaplexData(tokenAddress, connection).catch(() => null);
284
+ if (programId) {
285
+ const mint = await getMint(connection, tokenAddress, "confirmed", programId)
286
+ return {
287
+ address: tokenAddress.toString(),
288
+ decimals: mint.decimals,
289
+ name: metaplexData?.name || "",
290
+ symbol: metaplexData?.symbol || ""
291
+ }
292
+ }
276
293
  try {
277
294
  mint = await getMint(connection, tokenAddress, "confirmed", TOKEN_PROGRAM_ID)
278
295
 
@@ -286,8 +303,8 @@ export const getTokenInfo = async (tokenAddress: PublicKey, connection: Connecti
286
303
  return {
287
304
  address: tokenAddress.toString(),
288
305
  decimals: mint.decimals,
289
- name: "",
290
- symbol: ""
306
+ name: metaplexData?.name || "",
307
+ symbol: metaplexData?.symbol || ""
291
308
  }
292
309
  }
293
310
 
@@ -359,6 +376,35 @@ export const signAndSendTransaction = async (transaction: VersionedTransaction,
359
376
  console.log('Transaction successful, signature:', signature);
360
377
  return signature;
361
378
  }
379
+ export const discoverTokens = async (ownerAddress: PublicKey, connection: Connection): Promise<UserTokenBalance<PublicKey>[]> => {
380
+
381
+ const owner = new PublicKey(ownerAddress);
382
+ let response = await connection.getParsedTokenAccountsByOwner(owner, {
383
+ programId: TOKEN_PROGRAM_ID
384
+ });
385
+ console.log('response: ', response);
386
+ let tokens2022 = await connection.getParsedTokenAccountsByOwner(owner, {
387
+ programId: TOKEN_2022_PROGRAM_ID
388
+ });
389
+ console.log('tokens2022: ', tokens2022);
390
+
391
+ response.value = response.value.concat(tokens2022.value);
392
+ const tokens = await Promise.all(response.value.map(async (accountInfo) => {
393
+
394
+ const mintAddress = accountInfo.account.data["parsed"]["info"]["mint"]
395
+ const mint = await getTokenInfo(new PublicKey(mintAddress), connection)
396
+ return {
397
+ owner: accountInfo.account.data["parsed"]["info"]["owner"],
398
+ address: accountInfo.account.data["parsed"]["info"]["mint"],
399
+ decimals: accountInfo.account.data["parsed"]["info"]["tokenAmount"]["decimals"],
400
+ symbol: mint.symbol,
401
+ name: mint.name,
402
+ balance: accountInfo.account.data["parsed"]["info"]["tokenAmount"]["amount"]
403
+ }
404
+ }))
405
+
406
+ return tokens;
407
+ }
362
408
 
363
409
  //swap
364
410
  //you will. use jupiter for this
@@ -417,17 +463,21 @@ export const getJupiterQuote = async (
417
463
  export const buildJupiterSwapTransaction = async (
418
464
  quote: JupiterQuoteResponse,
419
465
  userPublicKey: string,
420
- prioritizationFeeLamports?: number
466
+ prioritizationFeeLamports?: number,
467
+ useSharedAccounts: boolean = true
421
468
  ): Promise<JupiterSwapResponse> => {
422
469
  console.log('buildJupiterSwapTransaction: Starting');
423
470
  console.log('User public key:', userPublicKey);
471
+ console.log('Use shared accounts:', useSharedAccounts);
472
+
424
473
  const priorityFee = prioritizationFeeLamports || 5000;
425
474
  console.log('Prioritization fee:', priorityFee);
475
+
426
476
  const body = {
427
477
  quoteResponse: quote,
428
478
  userPublicKey,
429
479
  wrapAndUnwrapSol: true,
430
- useSharedAccounts: true,
480
+ useSharedAccounts: useSharedAccounts,
431
481
  feeAccount: undefined,
432
482
  trackingAccount: undefined,
433
483
  computeUnitPriceMicroLamports: undefined,
@@ -459,9 +509,23 @@ export const buildJupiterSwapTransaction = async (
459
509
  try {
460
510
  const error = await response.json();
461
511
  console.log('Swap build error details:', error);
512
+
513
+ // Check if this is the shared accounts error
514
+ if (error.errorCode === 'NOT_SUPPORTED' &&
515
+ error.error?.includes('Simple AMMs are not supported with shared accounts')) {
516
+ console.log('Detected shared accounts incompatibility error');
517
+ throw new Error('SHARED_ACCOUNTS_NOT_SUPPORTED');
518
+ }
519
+
462
520
  throw new Error(`Jupiter swap transaction build failed: ${error.message || response.statusText}`);
463
521
  } catch (parseError) {
464
522
  console.log('Failed to parse error response:', parseError);
523
+
524
+ // Re-throw if it's our custom error
525
+ if (parseError instanceof Error && parseError.message === 'SHARED_ACCOUNTS_NOT_SUPPORTED') {
526
+ throw parseError;
527
+ }
528
+
465
529
  throw new Error(`Jupiter swap transaction build failed: ${response.statusText}`);
466
530
  }
467
531
  }
@@ -500,12 +564,38 @@ export const executeJupiterSwap = async (
500
564
  priceImpact: quote.priceImpactPct
501
565
  });
502
566
 
503
- console.log('Building swap transaction...');
504
- const swapResponse = await buildJupiterSwapTransaction(
505
- quote,
506
- swapParams.userPublicKey.toString()
507
- );
567
+ let swapResponse: JupiterSwapResponse;
568
+ let usedSharedAccounts = true;
508
569
 
570
+ try {
571
+ console.log('Building swap transaction with shared accounts enabled...');
572
+ swapResponse = await buildJupiterSwapTransaction(
573
+ quote,
574
+ swapParams.userPublicKey.toString(),
575
+ undefined,
576
+ true // Try with shared accounts first
577
+ );
578
+ console.log('Successfully built transaction with shared accounts');
579
+ } catch (error) {
580
+ if (error instanceof Error && error.message === 'SHARED_ACCOUNTS_NOT_SUPPORTED') {
581
+ console.log('Shared accounts not supported, retrying without shared accounts...');
582
+
583
+ // Retry without shared accounts
584
+ swapResponse = await buildJupiterSwapTransaction(
585
+ quote,
586
+ swapParams.userPublicKey.toString(),
587
+ undefined,
588
+ false // Retry with shared accounts disabled
589
+ );
590
+ usedSharedAccounts = false;
591
+ console.log('Successfully built transaction without shared accounts');
592
+ } else {
593
+ // Re-throw if it's a different error
594
+ throw error;
595
+ }
596
+ }
597
+
598
+ console.log('Transaction build method:', usedSharedAccounts ? 'with shared accounts' : 'without shared accounts');
509
599
  console.log('Deserializing transaction...');
510
600
  const swapTransactionBuf = Buffer.from(swapResponse.swapTransaction, 'base64');
511
601
  console.log('Transaction buffer length:', swapTransactionBuf.length);
@@ -530,7 +620,6 @@ export const executeJupiterSwap = async (
530
620
  }
531
621
  });
532
622
 
533
- // console.log('signature: ', signature);
534
623
  if (!signature) {
535
624
  console.log('Transaction failed to confirm');
536
625
  return {
@@ -541,6 +630,7 @@ export const executeJupiterSwap = async (
541
630
 
542
631
  const txSignature = signature.transaction.signatures[0];
543
632
  console.log('Swap successful! Signature:', txSignature);
633
+ console.log('Used shared accounts:', usedSharedAccounts);
544
634
 
545
635
  return {
546
636
  success: true,
@@ -631,4 +721,5 @@ export const validateJupiterTokens = async (
631
721
  console.log('Token validation failed:', error);
632
722
  return { valid: false, message: 'Failed to validate tokens' };
633
723
  }
634
- };
724
+ };
725
+