@deserialize/multi-vm-wallet 1.2.31 → 1.2.43
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/dist/IChainWallet.d.ts +2 -1
- package/dist/IChainWallet.js.map +1 -1
- package/dist/constant.js +1 -1
- package/dist/evm/evm.d.ts +2 -1
- package/dist/evm/evm.js +11 -1
- package/dist/evm/evm.js.map +1 -1
- package/dist/evm/transactionParsing.js +50 -30
- package/dist/evm/transactionParsing.js.map +1 -1
- package/dist/evm/utils.d.ts +12 -1
- package/dist/evm/utils.js +138 -2
- package/dist/evm/utils.js.map +1 -1
- package/dist/helpers/index.d.ts +3 -1
- package/dist/helpers/index.js +10 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/svm/svm.d.ts +2 -1
- package/dist/svm/svm.js +5 -0
- package/dist/svm/svm.js.map +1 -1
- package/dist/svm/utils.d.ts +16 -1
- package/dist/svm/utils.js +213 -6
- package/dist/svm/utils.js.map +1 -1
- package/dist/test.d.ts +1 -6
- package/dist/test.js +40 -16
- package/dist/test.js.map +1 -1
- package/dist/types.d.ts +150 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/utils/IChainWallet.ts +2 -1
- package/utils/constant.ts +1 -1
- package/utils/evm/evm.ts +14 -3
- package/utils/evm/transactionParsing.ts +62 -58
- package/utils/evm/utils.ts +161 -2
- package/utils/helpers/index.ts +12 -1
- package/utils/svm/svm.ts +9 -2
- package/utils/svm/utils.ts +265 -7
- package/utils/test.ts +47 -18
- package/utils/types.ts +199 -0
package/utils/svm/utils.ts
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
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, Transaction, TransactionInstruction, TransactionMessage, VersionedTransaction } from "@solana/web3.js";
|
|
5
|
-
import { ChainWalletConfig, UserTokenBalance, TokenInfo } from "../types";
|
|
5
|
+
import { ChainWalletConfig, UserTokenBalance, TokenInfo, SolanaNFT, NFT, NFTCollection } from "../types";
|
|
6
6
|
import { transactionSenderAndConfirmationWaiter } from "./transactionSender";
|
|
7
7
|
import { BN } from "bn.js";
|
|
8
|
-
import { generateSigner, percentAmount } from '@metaplex-foundation/umi'
|
|
8
|
+
import { generateSigner, percentAmount, publicKey } from '@metaplex-foundation/umi'
|
|
9
9
|
import {
|
|
10
10
|
createNft,
|
|
11
11
|
fetchDigitalAsset,
|
|
12
|
+
fetchAllDigitalAssetByOwner,
|
|
12
13
|
mplTokenMetadata,
|
|
13
14
|
} from '@metaplex-foundation/mpl-token-metadata'
|
|
14
15
|
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
|
|
@@ -283,20 +284,99 @@ const getMetaTokenMetaplexData = async (mintAddress: PublicKey, connection: Conn
|
|
|
283
284
|
const umi = createUmi(connection.rpcEndpoint).use(mplTokenMetadata())
|
|
284
285
|
const ass = await fetchDigitalAsset(umi, mintAddress.toBase58() as unknown as UmiPublicKey)
|
|
285
286
|
return ass.metadata
|
|
287
|
+
}
|
|
286
288
|
|
|
289
|
+
interface TokenMetadata {
|
|
290
|
+
name?: string;
|
|
291
|
+
symbol?: string;
|
|
292
|
+
description?: string;
|
|
293
|
+
image?: string;
|
|
294
|
+
twitter?: string;
|
|
295
|
+
telegram?: string;
|
|
296
|
+
website?: string;
|
|
297
|
+
discord?: string;
|
|
298
|
+
extensions?: {
|
|
299
|
+
twitter?: string;
|
|
300
|
+
telegram?: string;
|
|
301
|
+
website?: string;
|
|
302
|
+
discord?: string;
|
|
303
|
+
[key: string]: string | undefined;
|
|
304
|
+
};
|
|
287
305
|
}
|
|
288
306
|
|
|
307
|
+
const fetchMetadataFromUri = async (uri: string): Promise<TokenMetadata | null> => {
|
|
308
|
+
try {
|
|
309
|
+
// Convert IPFS URI to HTTP gateway if needed
|
|
310
|
+
let fetchUrl = uri;
|
|
311
|
+
if (uri.startsWith('ipfs://')) {
|
|
312
|
+
fetchUrl = uri.replace('ipfs://', 'https://ipfs.io/ipfs/');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
console.log('Fetching metadata from:', fetchUrl);
|
|
316
|
+
const response = await fetch(fetchUrl, {
|
|
317
|
+
headers: {
|
|
318
|
+
'Accept': 'application/json',
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
console.log('Failed to fetch metadata:', response.status);
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const metadata: TokenMetadata = await response.json();
|
|
328
|
+
console.log('Metadata fetched successfully');
|
|
329
|
+
return metadata;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
console.log('Error fetching metadata from URI:', error);
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
289
336
|
export const getTokenInfo = async (tokenAddress: PublicKey, connection: Connection, programId?: PublicKey): Promise<TokenInfo> => {
|
|
290
337
|
let mint: Mint
|
|
291
338
|
|
|
292
339
|
const metaplexData = await getMetaTokenMetaplexData(tokenAddress, connection).catch(() => null);
|
|
340
|
+
|
|
341
|
+
// Fetch metadata from URI if available
|
|
342
|
+
let uriMetadata: TokenMetadata | null = null;
|
|
343
|
+
if (metaplexData?.uri) {
|
|
344
|
+
uriMetadata = await fetchMetadataFromUri(metaplexData.uri).catch(() => null);
|
|
345
|
+
}
|
|
346
|
+
|
|
293
347
|
if (programId) {
|
|
294
348
|
const mint = await getMint(connection, tokenAddress, "confirmed", programId)
|
|
349
|
+
|
|
350
|
+
// Build social object from metadata
|
|
351
|
+
const social: TokenInfo['social'] = {};
|
|
352
|
+
if (uriMetadata?.twitter || uriMetadata?.extensions?.twitter) {
|
|
353
|
+
social.twitter = uriMetadata.twitter || uriMetadata.extensions?.twitter;
|
|
354
|
+
}
|
|
355
|
+
if (uriMetadata?.telegram || uriMetadata?.extensions?.telegram) {
|
|
356
|
+
social.telegram = uriMetadata.telegram || uriMetadata.extensions?.telegram;
|
|
357
|
+
}
|
|
358
|
+
if (uriMetadata?.discord || uriMetadata?.extensions?.discord) {
|
|
359
|
+
social.discord = uriMetadata.discord || uriMetadata.extensions?.discord;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Add any other social fields from extensions
|
|
363
|
+
if (uriMetadata?.extensions) {
|
|
364
|
+
Object.keys(uriMetadata.extensions).forEach(key => {
|
|
365
|
+
if (!['twitter', 'telegram', 'discord', 'website'].includes(key) && uriMetadata.extensions![key]) {
|
|
366
|
+
social[key] = uriMetadata.extensions![key];
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
295
371
|
return {
|
|
296
372
|
address: tokenAddress.toString(),
|
|
297
373
|
decimals: mint.decimals,
|
|
298
|
-
name: metaplexData?.name || "",
|
|
299
|
-
symbol: metaplexData?.symbol || ""
|
|
374
|
+
name: uriMetadata?.name || metaplexData?.name || "",
|
|
375
|
+
symbol: uriMetadata?.symbol || metaplexData?.symbol || "",
|
|
376
|
+
logoUrl: uriMetadata?.image || "",
|
|
377
|
+
description: uriMetadata?.description,
|
|
378
|
+
website: uriMetadata?.website || uriMetadata?.extensions?.website,
|
|
379
|
+
social: Object.keys(social).length > 0 ? social : undefined
|
|
300
380
|
}
|
|
301
381
|
}
|
|
302
382
|
try {
|
|
@@ -309,11 +389,36 @@ export const getTokenInfo = async (tokenAddress: PublicKey, connection: Connecti
|
|
|
309
389
|
|
|
310
390
|
}
|
|
311
391
|
|
|
392
|
+
// Build social object from metadata
|
|
393
|
+
const social: TokenInfo['social'] = {};
|
|
394
|
+
if (uriMetadata?.twitter || uriMetadata?.extensions?.twitter) {
|
|
395
|
+
social.twitter = uriMetadata.twitter || uriMetadata.extensions?.twitter;
|
|
396
|
+
}
|
|
397
|
+
if (uriMetadata?.telegram || uriMetadata?.extensions?.telegram) {
|
|
398
|
+
social.telegram = uriMetadata.telegram || uriMetadata.extensions?.telegram;
|
|
399
|
+
}
|
|
400
|
+
if (uriMetadata?.discord || uriMetadata?.extensions?.discord) {
|
|
401
|
+
social.discord = uriMetadata.discord || uriMetadata.extensions?.discord;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Add any other social fields from extensions
|
|
405
|
+
if (uriMetadata?.extensions) {
|
|
406
|
+
Object.keys(uriMetadata.extensions).forEach(key => {
|
|
407
|
+
if (!['twitter', 'telegram', 'discord', 'website'].includes(key) && uriMetadata.extensions![key]) {
|
|
408
|
+
social[key] = uriMetadata.extensions![key];
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
312
413
|
return {
|
|
313
414
|
address: tokenAddress.toString(),
|
|
314
415
|
decimals: mint.decimals,
|
|
315
|
-
name: metaplexData?.name || "",
|
|
316
|
-
symbol: metaplexData?.symbol || ""
|
|
416
|
+
name: uriMetadata?.name || metaplexData?.name || "",
|
|
417
|
+
symbol: uriMetadata?.symbol || metaplexData?.symbol || "",
|
|
418
|
+
logoUrl: uriMetadata?.image || "",
|
|
419
|
+
description: uriMetadata?.description,
|
|
420
|
+
website: uriMetadata?.website || uriMetadata?.extensions?.website,
|
|
421
|
+
social: Object.keys(social).length > 0 ? social : undefined
|
|
317
422
|
}
|
|
318
423
|
}
|
|
319
424
|
|
|
@@ -430,13 +535,16 @@ export const discoverTokens = async (ownerAddress: PublicKey, connection: Connec
|
|
|
430
535
|
|
|
431
536
|
const mintAddress = accountInfo.account.data["parsed"]["info"]["mint"]
|
|
432
537
|
const mint = await getTokenInfo(new PublicKey(mintAddress), connection)
|
|
538
|
+
// console.log('mint: ', mint);
|
|
433
539
|
return {
|
|
434
540
|
owner: accountInfo.account.data["parsed"]["info"]["owner"],
|
|
435
541
|
address: accountInfo.account.data["parsed"]["info"]["mint"],
|
|
436
542
|
decimals: accountInfo.account.data["parsed"]["info"]["tokenAmount"]["decimals"],
|
|
437
543
|
symbol: mint.symbol,
|
|
438
544
|
name: mint.name,
|
|
439
|
-
balance: accountInfo.account.data["parsed"]["info"]["tokenAmount"]["amount"]
|
|
545
|
+
balance: accountInfo.account.data["parsed"]["info"]["tokenAmount"]["amount"],
|
|
546
|
+
logoUrl: mint.logoUrl
|
|
547
|
+
|
|
440
548
|
}
|
|
441
549
|
}))
|
|
442
550
|
|
|
@@ -760,3 +868,153 @@ export const validateJupiterTokens = async (
|
|
|
760
868
|
}
|
|
761
869
|
};
|
|
762
870
|
|
|
871
|
+
export const transformSolanaNFTToUnified = (nft: SolanaNFT): NFT => {
|
|
872
|
+
return {
|
|
873
|
+
id: nft.mint,
|
|
874
|
+
name: nft.name,
|
|
875
|
+
symbol: nft.symbol,
|
|
876
|
+
description: '', // Will be populated from metadata URI if needed
|
|
877
|
+
image: undefined, // Will be populated from metadata URI if needed
|
|
878
|
+
uri: nft.uri,
|
|
879
|
+
collection: {
|
|
880
|
+
address: nft.mint,
|
|
881
|
+
name: nft.name,
|
|
882
|
+
verified: nft.creators?.some(c => c.verified) ?? false,
|
|
883
|
+
},
|
|
884
|
+
chainType: 'SVM',
|
|
885
|
+
balance: '1', // NFTs on Solana are typically quantity 1
|
|
886
|
+
creators: nft.creators,
|
|
887
|
+
sellerFeeBasisPoints: nft.sellerFeeBasisPoints,
|
|
888
|
+
tokenStandard: 'NonFungible',
|
|
889
|
+
raw: {
|
|
890
|
+
svm: nft
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
export const fetchWalletNfts = async (
|
|
896
|
+
walletAddress: PublicKey,
|
|
897
|
+
connection: Connection
|
|
898
|
+
): Promise<NFT[]> => {
|
|
899
|
+
console.log('fetchWalletNfts: Starting');
|
|
900
|
+
console.log('Wallet address:', walletAddress.toString());
|
|
901
|
+
|
|
902
|
+
try {
|
|
903
|
+
// Create UMI instance with the connection's RPC endpoint
|
|
904
|
+
const umi = createUmi(connection.rpcEndpoint).use(mplTokenMetadata());
|
|
905
|
+
console.log('UMI instance created with RPC endpoint:', connection.rpcEndpoint);
|
|
906
|
+
|
|
907
|
+
// Convert Solana PublicKey to UMI PublicKey
|
|
908
|
+
const owner = publicKey(walletAddress.toString());
|
|
909
|
+
console.log('Fetching NFTs for owner:', owner);
|
|
910
|
+
|
|
911
|
+
// Fetch all digital assets owned by the wallet
|
|
912
|
+
const assets = await fetchAllDigitalAssetByOwner(umi, owner);
|
|
913
|
+
console.log('Fetched assets count:', assets.length);
|
|
914
|
+
|
|
915
|
+
// Transform the assets into our SolanaNFT format
|
|
916
|
+
const solanaNfts: SolanaNFT[] = assets.map((asset) => {
|
|
917
|
+
const metadata = asset.metadata;
|
|
918
|
+
|
|
919
|
+
return {
|
|
920
|
+
mint: asset.publicKey.toString(),
|
|
921
|
+
name: metadata.name,
|
|
922
|
+
symbol: metadata.symbol,
|
|
923
|
+
uri: metadata.uri,
|
|
924
|
+
updateAuthority: metadata.updateAuthority.toString(),
|
|
925
|
+
sellerFeeBasisPoints: metadata.sellerFeeBasisPoints,
|
|
926
|
+
creators: metadata.creators && 'value' in metadata.creators && metadata.creators.value
|
|
927
|
+
? metadata.creators.value.map((creator) => ({
|
|
928
|
+
address: creator.address.toString(),
|
|
929
|
+
verified: creator.verified,
|
|
930
|
+
share: creator.share,
|
|
931
|
+
}))
|
|
932
|
+
: undefined,
|
|
933
|
+
};
|
|
934
|
+
});
|
|
935
|
+
|
|
936
|
+
// Transform to unified NFT format
|
|
937
|
+
const nfts = solanaNfts.map(transformSolanaNFTToUnified);
|
|
938
|
+
|
|
939
|
+
console.log('fetchWalletNfts: Successfully fetched', nfts.length, 'NFTs');
|
|
940
|
+
return nfts;
|
|
941
|
+
} catch (error) {
|
|
942
|
+
console.log('fetchWalletNfts: Error fetching NFTs:', error);
|
|
943
|
+
console.log('Error details:', error instanceof Error ? error.message : 'Unknown error');
|
|
944
|
+
throw error;
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Get NFT collection details for a specific collection on Solana
|
|
950
|
+
* @param walletAddress - User's wallet public key
|
|
951
|
+
* @param collectionAddress - The NFT collection address (can be collection mint or update authority)
|
|
952
|
+
* @param connection - Solana connection
|
|
953
|
+
* @returns NFTCollection object with collection details and user's NFTs in that collection
|
|
954
|
+
*
|
|
955
|
+
* Note: On Solana, collections can be identified by:
|
|
956
|
+
* - Collection mint address (from collection field in NFT metadata)
|
|
957
|
+
* - Update authority (who can modify the NFT metadata)
|
|
958
|
+
* This function filters by the collection.address field which is typically the collection mint
|
|
959
|
+
*/
|
|
960
|
+
export const getNFTCollection = async (
|
|
961
|
+
walletAddress: PublicKey,
|
|
962
|
+
collectionAddress: string,
|
|
963
|
+
connection: Connection
|
|
964
|
+
): Promise<NFTCollection | null> => {
|
|
965
|
+
console.log('getNFTCollection: Starting collection fetch');
|
|
966
|
+
console.log('Wallet address:', walletAddress.toString());
|
|
967
|
+
console.log('Collection Address:', collectionAddress);
|
|
968
|
+
|
|
969
|
+
try {
|
|
970
|
+
// Fetch all NFTs for the user
|
|
971
|
+
const allNfts = await fetchWalletNfts(walletAddress, connection);
|
|
972
|
+
|
|
973
|
+
console.log('getNFTCollection: Fetched', allNfts.length, 'total NFTs');
|
|
974
|
+
|
|
975
|
+
// Filter NFTs by collection address (case-insensitive comparison)
|
|
976
|
+
// On Solana, we match against the collection.address which is set from the mint
|
|
977
|
+
const collectionNfts = allNfts.filter(
|
|
978
|
+
nft => nft.collection.address.toLowerCase() === collectionAddress.toLowerCase()
|
|
979
|
+
);
|
|
980
|
+
|
|
981
|
+
console.log('getNFTCollection: Found', collectionNfts.length, 'NFTs in collection');
|
|
982
|
+
|
|
983
|
+
if (collectionNfts.length === 0) {
|
|
984
|
+
console.log('getNFTCollection: No NFTs found in this collection');
|
|
985
|
+
return null;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Extract collection metadata from the first NFT
|
|
989
|
+
const firstNft = collectionNfts[0];
|
|
990
|
+
const rawSvmNft = firstNft.raw?.svm;
|
|
991
|
+
|
|
992
|
+
// Aggregate creators from all NFTs (they should be the same for a collection)
|
|
993
|
+
const creators = firstNft.creators;
|
|
994
|
+
|
|
995
|
+
const collection: NFTCollection = {
|
|
996
|
+
address: collectionAddress,
|
|
997
|
+
name: firstNft.collection.name,
|
|
998
|
+
symbol: firstNft.symbol,
|
|
999
|
+
description: undefined, // Could be fetched from URI metadata if needed
|
|
1000
|
+
image: undefined, // Solana collections don't have a single image
|
|
1001
|
+
verified: firstNft.collection.verified,
|
|
1002
|
+
chainType: 'SVM',
|
|
1003
|
+
nfts: collectionNfts,
|
|
1004
|
+
totalOwned: collectionNfts.length,
|
|
1005
|
+
collectionMetadata: rawSvmNft ? {
|
|
1006
|
+
updateAuthority: rawSvmNft.updateAuthority,
|
|
1007
|
+
sellerFeeBasisPoints: rawSvmNft.sellerFeeBasisPoints,
|
|
1008
|
+
creators: creators,
|
|
1009
|
+
} : undefined,
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
console.log('getNFTCollection: Successfully created collection object');
|
|
1013
|
+
return collection;
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
console.error('getNFTCollection: Error fetching collection:', error);
|
|
1016
|
+
console.error('Error details:', error instanceof Error ? error.message : 'Unknown error');
|
|
1017
|
+
throw error;
|
|
1018
|
+
}
|
|
1019
|
+
};
|
|
1020
|
+
|
package/utils/test.ts
CHANGED
|
@@ -8,12 +8,15 @@ import { Connection, PublicKey, Keypair } from "@solana/web3.js";
|
|
|
8
8
|
import { SVMChainWallet, SVMVM, } from "./svm";
|
|
9
9
|
import { VM } from "./vm";
|
|
10
10
|
import { ChainWalletConfig } from "./types";
|
|
11
|
-
import { EVMChainWallet } from "./evm";
|
|
11
|
+
import { discoverNFTs, EVMChainWallet } from "./evm";
|
|
12
12
|
import { } from "./svm/transactionParsing";
|
|
13
13
|
import { getEVMTransactionHistory } from "./evm/transactionParsing";
|
|
14
14
|
import { createPublicClient, http, PublicClient } from "viem";
|
|
15
15
|
import { base, baseGoerli } from "viem/chains";
|
|
16
|
-
import { getTokenInfo } from "./svm/utils";
|
|
16
|
+
import { discoverTokens, fetchWalletNfts, getTokenInfo } from "./svm/utils";
|
|
17
|
+
import { JsonRpcProvider } from "ethers";
|
|
18
|
+
|
|
19
|
+
import { } from "@solana/spl-token-metadata"
|
|
17
20
|
// const mnemonic = GenerateNewMnemonic()
|
|
18
21
|
|
|
19
22
|
|
|
@@ -22,8 +25,8 @@ import { getTokenInfo } from "./svm/utils";
|
|
|
22
25
|
// const seed = VM.mnemonicToSeed(mnemonic)
|
|
23
26
|
const pKey = "4QxETeX9pndiF1XNghUiDTnZnHq3cfjmuPLBJysrgocsLq1yb8w96aPWALa8ZnRZWmDU4wM8Tg8d1ZRVVByj7uXE"
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
const x = testUserKeyPair instanceof Keypair;
|
|
28
|
+
const testUserKeyPair = Keypair.fromSecretKey(base58.decode(pKey));
|
|
29
|
+
// const x = testUserKeyPair instanceof Keypair;
|
|
27
30
|
const evePrivateKey = "0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318"
|
|
28
31
|
const evmPrivateKey2 = "0x0123456789012345678901234567890123456789012345678901234567890123"
|
|
29
32
|
const ogPrivKey = "d2d3f7117aa9a4c6e5d4affedd8a5ea624ffd82b2a1de81509e5913709b1ea72"
|
|
@@ -39,7 +42,7 @@ const ogPrivKey = "d2d3f7117aa9a4c6e5d4affedd8a5ea624ffd82b2a1de81509e5913709b1e
|
|
|
39
42
|
const chainConfig: ChainWalletConfig = {
|
|
40
43
|
chainId: 123456789,
|
|
41
44
|
name: "Solana",
|
|
42
|
-
rpcUrl: "https://solana-mainnet.g.alchemy.com/v2/
|
|
45
|
+
rpcUrl: "https://solana-mainnet.g.alchemy.com/v2/lhoyb3hc9ccT9NA_y2cfA",
|
|
43
46
|
explorerUrl: "https://explorer.solana.com",
|
|
44
47
|
nativeToken: { name: "Solana", symbol: "SOL", decimals: 9 },
|
|
45
48
|
confirmationNo: 1,
|
|
@@ -49,7 +52,7 @@ const chainConfig: ChainWalletConfig = {
|
|
|
49
52
|
const evmChainConfig: ChainWalletConfig = {
|
|
50
53
|
chainId: 8453,
|
|
51
54
|
name: "Ethereum",
|
|
52
|
-
rpcUrl: "https://eth-mainnet.g.alchemy.com/v2/
|
|
55
|
+
rpcUrl: "https://eth-mainnet.g.alchemy.com/v2/lhoyb3hc9ccT9NA_y2cfA",
|
|
53
56
|
explorerUrl: "https://explorer.ethereum.com",
|
|
54
57
|
nativeToken: { name: "Ethereum", symbol: "ETH", decimals: 18 },
|
|
55
58
|
confirmationNo: 1,
|
|
@@ -93,6 +96,10 @@ console.log('address: ', wallet.address);
|
|
|
93
96
|
const RPC_URL = chainConfig.rpcUrl;
|
|
94
97
|
const connection = new Connection(RPC_URL);
|
|
95
98
|
|
|
99
|
+
// const evmConnection = new JsonRpcProvider(evmChainConfig.rpcUrl, {
|
|
100
|
+
// chainId: evmChainConfig.chainId
|
|
101
|
+
// });
|
|
102
|
+
|
|
96
103
|
/**
|
|
97
104
|
* Fetches and logs the token metadata for a given mint address.
|
|
98
105
|
* @param mintAddress - The mint address of the token.
|
|
@@ -111,18 +118,40 @@ const connection = new Connection(RPC_URL);
|
|
|
111
118
|
// }).catch((error: any) => {
|
|
112
119
|
// console.error("Error fetching transaction history:", error);
|
|
113
120
|
// });
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
const client = createPublicClient({
|
|
122
|
+
chain: base,
|
|
123
|
+
transport: http(base.rpcUrls.default.http[0]),
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
getEVMTransactionHistory(client as PublicClient, "0x9C82CE0e125F61AdE50BC0c19638F6Ba93d71D5e", {
|
|
127
|
+
startBlock: BigInt(37427020)
|
|
128
|
+
// before: "0xabc..."
|
|
129
|
+
}).then((history: any) => {
|
|
130
|
+
console.log("EVM Transaction History:", history);
|
|
131
|
+
}).catch((error: any) => {
|
|
132
|
+
console.error("Error fetching EVM transaction history:", error);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// discoverNFTs("0x498581ff718922c3f8e6a244956af099b2652b2b", evmChainConfig).then(nfts => {
|
|
136
|
+
// console.log("Discovered NFTs:", nfts);
|
|
137
|
+
// }).catch(error => {
|
|
138
|
+
// console.error("Error discovering NFTs:", error);
|
|
139
|
+
// });
|
|
140
|
+
|
|
141
|
+
// fetchWalletNfts(new PublicKey("LebronkTYWjc5J1gtqntdMcbhBXgwRgYcCxMA8JMA17"), connection).then(nfts => {
|
|
142
|
+
// console.log("Discovered NFTs:", nfts);
|
|
143
|
+
// }).catch(error => {
|
|
144
|
+
// console.error("Error discovering NFTs:", error);
|
|
145
|
+
// });
|
|
146
|
+
|
|
147
|
+
// discoverTokens(new PublicKey("8AXoqNjEVyhhe43ckDEsYiphWxTBH2oWXkKLZAbCNWLL"), connection).then(tokens => {
|
|
148
|
+
// console.log("Discovered Tokens:", tokens);
|
|
149
|
+
// }).catch(error => {
|
|
150
|
+
// console.error("Error discovering Tokens:", error);
|
|
126
151
|
// });
|
|
127
152
|
|
|
128
153
|
|
|
154
|
+
// //IIFE
|
|
155
|
+
// (async () => {
|
|
156
|
+
// //GET TOKEN LOGO using @solana/spl-token-metadata
|
|
157
|
+
// })();
|
package/utils/types.ts
CHANGED
|
@@ -24,6 +24,15 @@ export interface TokenInfo {
|
|
|
24
24
|
name: string;
|
|
25
25
|
symbol: string;
|
|
26
26
|
decimals: number;
|
|
27
|
+
logoUrl?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
website?: string;
|
|
30
|
+
social?: {
|
|
31
|
+
twitter?: string;
|
|
32
|
+
discord?: string;
|
|
33
|
+
telegram?: string;
|
|
34
|
+
[key: string]: string | undefined;
|
|
35
|
+
};
|
|
27
36
|
}
|
|
28
37
|
|
|
29
38
|
export interface UserTokenBalance<AddressType> extends TokenInfo {
|
|
@@ -39,6 +48,196 @@ export interface NFTInfo {
|
|
|
39
48
|
image?: string;
|
|
40
49
|
}
|
|
41
50
|
|
|
51
|
+
// Unified NFT interface for both EVM and SVM chains
|
|
52
|
+
export interface NFT {
|
|
53
|
+
// Unique identifier
|
|
54
|
+
id: string; // For Solana: mint address, For EVM: contractAddress:tokenId
|
|
55
|
+
|
|
56
|
+
// Basic metadata
|
|
57
|
+
name: string;
|
|
58
|
+
symbol?: string;
|
|
59
|
+
description: string;
|
|
60
|
+
image?: string;
|
|
61
|
+
uri: string; // Metadata URI
|
|
62
|
+
|
|
63
|
+
// Collection/Contract info
|
|
64
|
+
collection: {
|
|
65
|
+
address: string;
|
|
66
|
+
name: string;
|
|
67
|
+
verified?: boolean;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Chain-specific data
|
|
71
|
+
chainType: 'SVM' | 'EVM';
|
|
72
|
+
|
|
73
|
+
// Ownership
|
|
74
|
+
balance?: string; // Quantity owned (useful for ERC1155)
|
|
75
|
+
|
|
76
|
+
// Optional metadata
|
|
77
|
+
attributes?: Array<{
|
|
78
|
+
trait_type: string;
|
|
79
|
+
value: string | number;
|
|
80
|
+
display_type?: string;
|
|
81
|
+
}>;
|
|
82
|
+
|
|
83
|
+
// Creators/Royalties (mainly for Solana)
|
|
84
|
+
creators?: Array<{
|
|
85
|
+
address: string;
|
|
86
|
+
verified: boolean;
|
|
87
|
+
share: number;
|
|
88
|
+
}>;
|
|
89
|
+
|
|
90
|
+
// Additional metadata
|
|
91
|
+
sellerFeeBasisPoints?: number; // Royalty percentage
|
|
92
|
+
tokenStandard?: string; // e.g., "ERC721", "ERC1155", "NonFungible", etc.
|
|
93
|
+
|
|
94
|
+
// Spam detection (mainly for EVM)
|
|
95
|
+
isSpam?: boolean;
|
|
96
|
+
|
|
97
|
+
// Raw chain-specific data for advanced use cases
|
|
98
|
+
raw?: {
|
|
99
|
+
svm?: SolanaNFT;
|
|
100
|
+
evm?: EVMNFT;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// NFT Collection interface for both EVM and SVM chains
|
|
105
|
+
export interface NFTCollection {
|
|
106
|
+
// Collection identifier
|
|
107
|
+
address: string;
|
|
108
|
+
|
|
109
|
+
// Collection metadata
|
|
110
|
+
name: string;
|
|
111
|
+
symbol?: string;
|
|
112
|
+
description?: string;
|
|
113
|
+
image?: string;
|
|
114
|
+
|
|
115
|
+
// Verification status
|
|
116
|
+
verified?: boolean;
|
|
117
|
+
|
|
118
|
+
// Chain-specific data
|
|
119
|
+
chainType: 'SVM' | 'EVM';
|
|
120
|
+
|
|
121
|
+
// NFTs owned by the user in this collection
|
|
122
|
+
nfts: NFT[];
|
|
123
|
+
|
|
124
|
+
// Collection statistics
|
|
125
|
+
totalOwned: number; // Number of NFTs owned by user in this collection
|
|
126
|
+
|
|
127
|
+
// Collection-level metadata (for EVM)
|
|
128
|
+
contractMetadata?: {
|
|
129
|
+
tokenType?: string; // ERC721, ERC1155, etc.
|
|
130
|
+
totalSupply?: string;
|
|
131
|
+
contractDeployer?: string;
|
|
132
|
+
deployedBlockNumber?: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Collection-level metadata (for SVM)
|
|
136
|
+
collectionMetadata?: {
|
|
137
|
+
updateAuthority?: string;
|
|
138
|
+
sellerFeeBasisPoints?: number;
|
|
139
|
+
creators?: Array<{
|
|
140
|
+
address: string;
|
|
141
|
+
verified: boolean;
|
|
142
|
+
share: number;
|
|
143
|
+
}>;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// OpenSea metadata (for EVM)
|
|
147
|
+
openSeaMetadata?: {
|
|
148
|
+
collectionSlug?: string;
|
|
149
|
+
floorPrice?: number;
|
|
150
|
+
safelistRequestStatus?: string;
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface SolanaNFT {
|
|
155
|
+
mint: string;
|
|
156
|
+
name: string;
|
|
157
|
+
symbol: string;
|
|
158
|
+
uri: string;
|
|
159
|
+
updateAuthority: string;
|
|
160
|
+
sellerFeeBasisPoints: number;
|
|
161
|
+
creators?: Array<{
|
|
162
|
+
address: string;
|
|
163
|
+
verified: boolean;
|
|
164
|
+
share: number;
|
|
165
|
+
}>;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface EVMNFTContract {
|
|
169
|
+
address: string;
|
|
170
|
+
name: string;
|
|
171
|
+
symbol: string;
|
|
172
|
+
totalSupply: string;
|
|
173
|
+
tokenType: string;
|
|
174
|
+
contractDeployer: string;
|
|
175
|
+
deployedBlockNumber: number;
|
|
176
|
+
isSpam: boolean;
|
|
177
|
+
spamClassifications?: string[];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export interface EVMNFTOpenSeaMetadata {
|
|
181
|
+
collectionName: string;
|
|
182
|
+
collectionSlug: string;
|
|
183
|
+
floorPrice?: number;
|
|
184
|
+
safelistRequestStatus: string;
|
|
185
|
+
imageUrl?: string;
|
|
186
|
+
lastIngestedAt: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface EVMNFTImage {
|
|
190
|
+
cachedUrl?: string;
|
|
191
|
+
thumbnailUrl?: string;
|
|
192
|
+
pngUrl?: string;
|
|
193
|
+
contentType?: string;
|
|
194
|
+
size?: number;
|
|
195
|
+
originalUrl?: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface EVMNFTAttribute {
|
|
199
|
+
trait_type: string;
|
|
200
|
+
value: string | number;
|
|
201
|
+
display_type?: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface EVMNFTRawMetadata {
|
|
205
|
+
tokenUri?: string;
|
|
206
|
+
metadata?: {
|
|
207
|
+
name?: string;
|
|
208
|
+
description?: string;
|
|
209
|
+
image?: string;
|
|
210
|
+
attributes?: EVMNFTAttribute[];
|
|
211
|
+
[key: string]: any;
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface EVMNFTMint {
|
|
216
|
+
mintAddress?: string;
|
|
217
|
+
blockNumber?: number;
|
|
218
|
+
timestamp?: string;
|
|
219
|
+
transactionHash?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface EVMNFT {
|
|
223
|
+
contract: EVMNFTContract;
|
|
224
|
+
openSeaMetadata?: EVMNFTOpenSeaMetadata;
|
|
225
|
+
tokenId: string;
|
|
226
|
+
tokenType: string;
|
|
227
|
+
name: string;
|
|
228
|
+
description: string;
|
|
229
|
+
balance: string;
|
|
230
|
+
image?: EVMNFTImage;
|
|
231
|
+
raw?: EVMNFTRawMetadata;
|
|
232
|
+
mint?: EVMNFTMint;
|
|
233
|
+
timeLastUpdated: string;
|
|
234
|
+
acquiredAt?: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface EVMNFTResponse {
|
|
238
|
+
data: EVMNFT[];
|
|
239
|
+
}
|
|
240
|
+
|
|
42
241
|
export interface TransactionResult {
|
|
43
242
|
hash: string;
|
|
44
243
|
success: boolean;
|