@adelos/sdk 0.1.2 → 0.1.5

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/index.js CHANGED
@@ -30,24 +30,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ADELOS_CONFIG: () => ADELOS_CONFIG,
33
34
  AdelosIndexer: () => AdelosIndexer,
34
35
  AdelosSDK: () => AdelosSDK,
35
36
  IDL: () => IDL,
36
- LIGHT_PROGRAM_IDS: () => LIGHT_PROGRAM_IDS,
37
- LightClient: () => LightClient,
38
37
  MEMO_PREFIX: () => MEMO_PREFIX,
39
38
  MEMO_PROGRAM_ID: () => MEMO_PROGRAM_ID,
40
39
  PROGRAM_ID: () => PROGRAM_ID,
41
- PROGRAM_IDS: () => PROGRAM_IDS,
42
- REGISTRY_ACCOUNT_SIZE: () => REGISTRY_ACCOUNT_SIZE,
43
40
  REGISTRY_SEED: () => REGISTRY_SEED,
44
- RPC_URLS: () => RPC_URLS,
41
+ RPC_URL: () => RPC_URL,
45
42
  STEALTH_DOMAIN: () => STEALTH_DOMAIN,
46
43
  bytesToHex: () => bytesToHex,
47
44
  computeSharedSecret: () => computeSharedSecret,
48
45
  computeSharedSecretAsRecipient: () => computeSharedSecretAsRecipient,
49
46
  createIndexer: () => createIndexer,
50
- createLightClient: () => createLightClient,
51
47
  deriveRegistryPda: () => deriveRegistryPda,
52
48
  deriveStealthPubkey: () => deriveStealthPubkey,
53
49
  generateEphemeralKeypair: () => generateEphemeralKeypair,
@@ -55,41 +51,35 @@ __export(index_exports, {
55
51
  generateStealthMemo: () => generateStealthMemo,
56
52
  getDiscriminator: () => getDiscriminator,
57
53
  hexToBytes: () => hexToBytes,
58
- isStealthTransactionForMe: () => isStealthTransactionForMe,
59
54
  isValidMetaPubkey: () => isValidMetaPubkey,
60
55
  parseStealthMemo: () => parseStealthMemo,
61
56
  recoverStealthSecretKey: () => recoverStealthSecretKey
62
57
  });
63
58
  module.exports = __toCommonJS(index_exports);
64
- var import_web35 = require("@solana/web3.js");
59
+ var import_web34 = require("@solana/web3.js");
65
60
 
66
61
  // src/constants.ts
67
62
  var import_web3 = require("@solana/web3.js");
68
- var PROGRAM_IDS = {
69
- devnet: new import_web3.PublicKey("7T1UxHJ6psKiQheKZXxANu6mhgsmgaX55eNKZZL5u4Rp"),
70
- "mainnet-beta": new import_web3.PublicKey("7T1UxHJ6psKiQheKZXxANu6mhgsmgaX55eNKZZL5u4Rp"),
71
- // Update when deployed to mainnet
72
- localnet: new import_web3.PublicKey("7T1UxHJ6psKiQheKZXxANu6mhgsmgaX55eNKZZL5u4Rp")
73
- };
74
- var RPC_URLS = {
75
- devnet: (0, import_web3.clusterApiUrl)("devnet"),
76
- "mainnet-beta": (0, import_web3.clusterApiUrl)("mainnet-beta"),
77
- localnet: "http://localhost:8899"
63
+ var ADELOS_CONFIG = {
64
+ PROGRAM_ID: new import_web3.PublicKey("7T1UxHJ6psKiQheKZXxANu6mhgsmgaX55eNKZZL5u4Rp"),
65
+ RPC_URL: (0, import_web3.clusterApiUrl)("devnet"),
66
+ MEMO_PROGRAM_ID: new import_web3.PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
67
+ REGISTRY_SEED: "registry",
68
+ MEMO_PREFIX: "ADLSv1:",
69
+ STEALTH_DOMAIN: "adelos:stealth:v1"
78
70
  };
79
- var PROGRAM_ID = PROGRAM_IDS.devnet;
80
- var MEMO_PROGRAM_ID = new import_web3.PublicKey(
81
- "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"
82
- );
83
- var REGISTRY_SEED = "registry";
84
- var REGISTRY_ACCOUNT_SIZE = 8 + 32 + 32 + 1;
85
- var MEMO_PREFIX = "ADLSv1:";
86
- var STEALTH_DOMAIN = "adelos:stealth:v1";
71
+ var PROGRAM_ID = ADELOS_CONFIG.PROGRAM_ID;
72
+ var RPC_URL = ADELOS_CONFIG.RPC_URL;
73
+ var MEMO_PROGRAM_ID = ADELOS_CONFIG.MEMO_PROGRAM_ID;
74
+ var REGISTRY_SEED = ADELOS_CONFIG.REGISTRY_SEED;
75
+ var MEMO_PREFIX = ADELOS_CONFIG.MEMO_PREFIX;
76
+ var STEALTH_DOMAIN = ADELOS_CONFIG.STEALTH_DOMAIN;
87
77
 
88
78
  // src/utils.ts
89
79
  var import_web32 = require("@solana/web3.js");
90
- function deriveRegistryPda(owner, programId = PROGRAM_ID) {
80
+ function deriveRegistryPda(owner, programId = ADELOS_CONFIG.PROGRAM_ID) {
91
81
  return import_web32.PublicKey.findProgramAddressSync(
92
- [Buffer.from(REGISTRY_SEED), owner.toBuffer()],
82
+ [Buffer.from(ADELOS_CONFIG.REGISTRY_SEED), owner.toBuffer()],
93
83
  programId
94
84
  );
95
85
  }
@@ -362,65 +352,52 @@ var IDL = {
362
352
 
363
353
  // src/crypto.ts
364
354
  var import_sha256 = require("@noble/hashes/sha256");
355
+ var import_sha512 = require("@noble/hashes/sha512");
365
356
  var ed = __toESM(require("@noble/ed25519"));
357
+ ed.etc.sha512Sync = (...m) => {
358
+ const h = import_sha512.sha512.create();
359
+ m.forEach((msg) => h.update(msg));
360
+ return h.digest();
361
+ };
362
+ var encoder = new TextEncoder();
366
363
  function generateEphemeralKeypair() {
367
364
  const secretKey = ed.utils.randomPrivateKey();
368
- const publicKey = ed.getPublicKey(secretKey);
369
- return { secretKey, publicKey };
365
+ return { secretKey, publicKey: ed.getPublicKey(secretKey) };
370
366
  }
371
- async function computeSharedSecret(ephemeralSk, recipientMetaPubkey) {
372
- const point = ed.ExtendedPoint.fromHex(recipientMetaPubkey);
373
- const scalar = ed.etc.mod(
374
- BigInt("0x" + bytesToHex(ephemeralSk)),
375
- ed.CURVE.n
376
- );
377
- const sharedPoint = point.multiply(scalar);
378
- const sharedBytes = sharedPoint.toRawBytes();
379
- return (0, import_sha256.sha256)(sharedBytes);
367
+ function computeSharedSecret(ephemeralSk, recipientMetaPk) {
368
+ const point = ed.ExtendedPoint.fromHex(bytesToHex(recipientMetaPk));
369
+ const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(ephemeralSk)), ed.CURVE.n);
370
+ return (0, import_sha256.sha256)(point.multiply(scalar).toRawBytes());
380
371
  }
381
- function deriveStealthPubkey(metaPubkey, sharedSecret) {
382
- const scalarBytes = (0, import_sha256.sha256)(
383
- new Uint8Array([...sharedSecret, ...Buffer.from(STEALTH_DOMAIN)])
384
- );
385
- const scalar = ed.etc.mod(
386
- BigInt("0x" + bytesToHex(scalarBytes)),
387
- ed.CURVE.n
388
- );
389
- const scalarTimesG = ed.ExtendedPoint.BASE.multiply(scalar);
390
- const metaPoint = ed.ExtendedPoint.fromHex(metaPubkey);
391
- const stealthPoint = metaPoint.add(scalarTimesG);
372
+ function computeSharedSecretAsRecipient(metaSk, ephemeralPk) {
373
+ const secretKey = metaSk.length === 64 ? metaSk.slice(0, 32) : metaSk;
374
+ const point = ed.ExtendedPoint.fromHex(bytesToHex(ephemeralPk));
375
+ const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(secretKey)), ed.CURVE.n);
376
+ return (0, import_sha256.sha256)(point.multiply(scalar).toRawBytes());
377
+ }
378
+ function deriveStealthPubkey(metaPk, sharedSecret) {
379
+ const domain = encoder.encode(ADELOS_CONFIG.STEALTH_DOMAIN);
380
+ const scalarBytes = (0, import_sha256.sha256)(new Uint8Array([...sharedSecret, ...domain]));
381
+ const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(scalarBytes)), ed.CURVE.n);
382
+ const metaPoint = ed.ExtendedPoint.fromHex(bytesToHex(metaPk));
383
+ const stealthPoint = metaPoint.add(ed.ExtendedPoint.BASE.multiply(scalar));
392
384
  return stealthPoint.toRawBytes();
393
385
  }
394
386
  function recoverStealthSecretKey(metaSk, sharedSecret) {
395
- const scalarBytes = (0, import_sha256.sha256)(
396
- new Uint8Array([...sharedSecret, ...Buffer.from(STEALTH_DOMAIN)])
397
- );
398
- const scalar = ed.etc.mod(
399
- BigInt("0x" + bytesToHex(scalarBytes)),
400
- ed.CURVE.n
401
- );
402
- const metaScalar = ed.etc.mod(
403
- BigInt("0x" + bytesToHex(metaSk)),
404
- ed.CURVE.n
405
- );
387
+ const domain = encoder.encode(ADELOS_CONFIG.STEALTH_DOMAIN);
388
+ const scalarBytes = (0, import_sha256.sha256)(new Uint8Array([...sharedSecret, ...domain]));
389
+ const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(scalarBytes)), ed.CURVE.n);
390
+ const metaScalar = ed.etc.mod(BigInt("0x" + bytesToHex(metaSk)), ed.CURVE.n);
406
391
  const stealthScalar = ed.etc.mod(metaScalar + scalar, ed.CURVE.n);
407
392
  const hex = stealthScalar.toString(16).padStart(64, "0");
408
393
  return hexToBytes(hex);
409
394
  }
410
- async function computeSharedSecretAsRecipient(metaSk, ephemeralPubkey) {
411
- const point = ed.ExtendedPoint.fromHex(ephemeralPubkey);
412
- const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(metaSk)), ed.CURVE.n);
413
- const sharedPoint = point.multiply(scalar);
414
- const sharedBytes = sharedPoint.toRawBytes();
415
- return (0, import_sha256.sha256)(sharedBytes);
416
- }
417
395
  function generateStealthMemo(ephemeralPubkey) {
418
- const pubkeyHex = bytesToHex(ephemeralPubkey);
419
- return `${MEMO_PREFIX}${pubkeyHex}`;
396
+ return `${ADELOS_CONFIG.MEMO_PREFIX}${bytesToHex(ephemeralPubkey)}`;
420
397
  }
421
398
  function parseStealthMemo(memo) {
422
- if (!memo.startsWith(MEMO_PREFIX)) return null;
423
- const pubkeyHex = memo.slice(MEMO_PREFIX.length);
399
+ if (!memo.startsWith(ADELOS_CONFIG.MEMO_PREFIX)) return null;
400
+ const pubkeyHex = memo.slice(ADELOS_CONFIG.MEMO_PREFIX.length);
424
401
  if (pubkeyHex.length !== 64) return null;
425
402
  try {
426
403
  return hexToBytes(pubkeyHex);
@@ -428,613 +405,115 @@ function parseStealthMemo(memo) {
428
405
  return null;
429
406
  }
430
407
  }
431
- async function generateStealthAddress(recipientMetaPubkey) {
408
+ function generateStealthAddress(recipientMetaPk) {
432
409
  const ephemeralKeypair = generateEphemeralKeypair();
433
- const sharedSecret = await computeSharedSecret(
434
- ephemeralKeypair.secretKey,
435
- recipientMetaPubkey
436
- );
437
- const stealthPubkey = deriveStealthPubkey(recipientMetaPubkey, sharedSecret);
410
+ const sharedSecret = computeSharedSecret(ephemeralKeypair.secretKey, recipientMetaPk);
411
+ const stealthPubkey = deriveStealthPubkey(recipientMetaPk, sharedSecret);
438
412
  const memo = generateStealthMemo(ephemeralKeypair.publicKey);
439
- return {
440
- stealthPubkey,
441
- ephemeralKeypair,
442
- sharedSecret,
443
- memo
444
- };
445
- }
446
- async function isStealthTransactionForMe(metaSk, metaPubkey, ephemeralPubkey, targetAddress) {
447
- const sharedSecret = await computeSharedSecretAsRecipient(
448
- metaSk,
449
- ephemeralPubkey
450
- );
451
- const expectedStealth = deriveStealthPubkey(metaPubkey, sharedSecret);
452
- return bytesToHex(expectedStealth) === bytesToHex(targetAddress);
453
- }
454
-
455
- // src/light.ts
456
- var import_web33 = require("@solana/web3.js");
457
- var LIGHT_PROGRAM_IDS = {
458
- LIGHT_SYSTEM_PROGRAM: new import_web33.PublicKey(
459
- "SySTEM1eSU2p4BGQfQpimFEWWSC1XDFeun3Nqzz3rT7"
460
- ),
461
- COMPRESSED_TOKEN_PROGRAM: new import_web33.PublicKey(
462
- "cTokenmWW8bLPjZEBAUgYy3zKxQZW6VKi7bqNFEVv3m"
463
- ),
464
- ACCOUNT_COMPRESSION_PROGRAM: new import_web33.PublicKey(
465
- "compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq"
466
- )
467
- };
468
- var LightClient = class _LightClient {
469
- constructor(connection, config) {
470
- this.connection = connection;
471
- this.config = config;
472
- }
473
- /**
474
- * Creates a new Light Protocol client
475
- *
476
- * @param rpcUrl - Solana RPC URL with Light Protocol support
477
- * @returns LightClient instance
478
- */
479
- static create(rpcUrl) {
480
- const connection = new import_web33.Connection(rpcUrl, "confirmed");
481
- return new _LightClient(connection, { rpcUrl });
482
- }
483
- /**
484
- * Gets compressed SOL balance for an address
485
- *
486
- * @param owner - Owner public key
487
- * @returns Compressed SOL balance in lamports
488
- */
489
- async getCompressedSolBalance(owner) {
490
- try {
491
- const response = await fetch(this.config.rpcUrl, {
492
- method: "POST",
493
- headers: { "Content-Type": "application/json" },
494
- body: JSON.stringify({
495
- jsonrpc: "2.0",
496
- id: 1,
497
- method: "getCompressedAccountsByOwner",
498
- params: [owner.toBase58()]
499
- })
500
- });
501
- const data = await response.json();
502
- if (data.error) {
503
- console.warn("Light RPC error:", data.error);
504
- return BigInt(0);
505
- }
506
- const accounts = data.result?.items || [];
507
- let total = BigInt(0);
508
- for (const acc of accounts) {
509
- total += BigInt(acc.lamports || 0);
510
- }
511
- return total;
512
- } catch (error) {
513
- console.warn("Failed to get compressed balance:", error);
514
- return BigInt(0);
515
- }
516
- }
517
- /**
518
- * Gets compressed token balances for an address
519
- *
520
- * @param owner - Owner public key
521
- * @param mint - Optional token mint to filter
522
- * @returns Array of compressed token balances
523
- */
524
- async getCompressedTokenBalances(owner, mint) {
525
- try {
526
- const response = await fetch(this.config.rpcUrl, {
527
- method: "POST",
528
- headers: { "Content-Type": "application/json" },
529
- body: JSON.stringify({
530
- jsonrpc: "2.0",
531
- id: 1,
532
- method: "getCompressedTokenAccountsByOwner",
533
- params: [owner.toBase58(), mint?.toBase58()]
534
- })
535
- });
536
- const data = await response.json();
537
- if (data.error) {
538
- console.warn("Light RPC error:", data.error);
539
- return [];
540
- }
541
- const balancesByMint = /* @__PURE__ */ new Map();
542
- const accounts = data.result?.items || [];
543
- for (const acc of accounts) {
544
- const mintStr = acc.mint;
545
- if (!balancesByMint.has(mintStr)) {
546
- balancesByMint.set(mintStr, {
547
- mint: new import_web33.PublicKey(mintStr),
548
- amount: BigInt(0),
549
- accounts: []
550
- });
551
- }
552
- const balance = balancesByMint.get(mintStr);
553
- balance.amount += BigInt(acc.amount || 0);
554
- balance.accounts.push({
555
- hash: acc.hash,
556
- owner: new import_web33.PublicKey(acc.owner),
557
- lamports: acc.lamports || 0,
558
- data: new Uint8Array(acc.data || []),
559
- tree: new import_web33.PublicKey(acc.tree),
560
- leafIndex: acc.leafIndex
561
- });
562
- }
563
- return Array.from(balancesByMint.values());
564
- } catch (error) {
565
- console.warn("Failed to get compressed token balances:", error);
566
- return [];
567
- }
568
- }
569
- /**
570
- * Creates a compressed SOL transfer instruction
571
- *
572
- * Note: This creates the instruction data structure.
573
- * Actual ZK proof generation requires Light Protocol SDK.
574
- *
575
- * @param from - Sender public key
576
- * @param to - Recipient public key (can be stealth address)
577
- * @param amount - Amount in lamports
578
- * @returns Transaction instruction (placeholder)
579
- */
580
- createCompressedTransferInstruction(from, to, amount) {
581
- const data = Buffer.alloc(72);
582
- data.write("compressed_transfer", 0);
583
- data.writeBigUInt64LE(amount, 32);
584
- return new import_web33.TransactionInstruction({
585
- keys: [
586
- { pubkey: from, isSigner: true, isWritable: true },
587
- { pubkey: to, isSigner: false, isWritable: true },
588
- {
589
- pubkey: LIGHT_PROGRAM_IDS.LIGHT_SYSTEM_PROGRAM,
590
- isSigner: false,
591
- isWritable: false
592
- }
593
- ],
594
- programId: LIGHT_PROGRAM_IDS.LIGHT_SYSTEM_PROGRAM,
595
- data
596
- });
597
- }
598
- /**
599
- * Compresses SOL from regular account to compressed account
600
- *
601
- * @param owner - Owner public key
602
- * @param amount - Amount in lamports to compress
603
- * @returns Transaction (unsigned)
604
- */
605
- async createCompressSolTransaction(owner, amount) {
606
- const instruction = this.createCompressedTransferInstruction(
607
- owner,
608
- owner,
609
- // Compress to self
610
- amount
611
- );
612
- const transaction = new import_web33.Transaction().add(instruction);
613
- transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
614
- transaction.feePayer = owner;
615
- return transaction;
616
- }
617
- /**
618
- * Decompresses SOL from compressed account to regular account
619
- *
620
- * @param owner - Owner public key
621
- * @param amount - Amount in lamports to decompress
622
- * @returns Transaction (unsigned)
623
- */
624
- async createDecompressSolTransaction(owner, amount) {
625
- const instruction = this.createCompressedTransferInstruction(
626
- owner,
627
- owner,
628
- amount
629
- );
630
- const transaction = new import_web33.Transaction().add(instruction);
631
- transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
632
- transaction.feePayer = owner;
633
- return transaction;
634
- }
635
- /**
636
- * Creates a stealth compressed transfer
637
- * Combines stealth addressing with ZK-compression
638
- *
639
- * @param from - Sender public key
640
- * @param stealthPubkey - Derived stealth address for recipient
641
- * @param amount - Amount in lamports
642
- * @param memo - Stealth memo containing ephemeral pubkey
643
- * @returns Transaction (unsigned)
644
- */
645
- async createStealthCompressedTransfer(from, stealthPubkey, amount, memo) {
646
- const stealthAddress = new import_web33.PublicKey(stealthPubkey);
647
- const transferIx = this.createCompressedTransferInstruction(
648
- from,
649
- stealthAddress,
650
- amount
651
- );
652
- const memoIx = new import_web33.TransactionInstruction({
653
- keys: [],
654
- programId: MEMO_PROGRAM_ID,
655
- data: Buffer.from(memo, "utf-8")
656
- });
657
- const transaction = new import_web33.Transaction().add(transferIx).add(memoIx);
658
- transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
659
- transaction.feePayer = from;
660
- return transaction;
661
- }
662
- };
663
- function createLightClient(rpcUrl) {
664
- return LightClient.create(rpcUrl);
413
+ return { stealthPubkey, ephemeralKeypair, sharedSecret, memo };
665
414
  }
666
415
 
667
416
  // src/indexer.ts
668
- var import_web34 = require("@solana/web3.js");
417
+ var import_web33 = require("@solana/web3.js");
669
418
  var AdelosIndexer = class _AdelosIndexer {
670
419
  constructor(config) {
671
- this.isScanning = false;
672
- this.scanIntervalId = null;
673
- this.config = config;
674
- this.connection = new import_web34.Connection(config.rpcUrl, "confirmed");
420
+ this.connection = new import_web33.Connection(config.rpcUrl, "confirmed");
421
+ this.heliusApiKey = config.heliusApiKey;
675
422
  }
676
- /**
677
- * Creates an indexer instance
678
- */
679
423
  static create(config) {
680
424
  return new _AdelosIndexer(config);
681
425
  }
682
- /**
683
- * Scans recent transactions for stealth transfers to a recipient
684
- *
685
- * IMPORTANT: We scan the Memo Program, NOT the metaPubkey!
686
- * The metaPubkey should never appear in transactions - that's the whole point of stealth addresses.
687
- * We scan memo program for ADLSv1: prefix, then use Trial Decryption to check if it's for us.
688
- *
689
- * @param metaSk - Recipient's meta secret key
690
- * @param metaPubkey - Recipient's meta public key
691
- * @param limit - Number of transactions to scan
692
- * @returns Array of stealth transactions for this recipient
693
- */
694
- async scanForStealthTransfers(metaSk, metaPubkey, limit = 100) {
695
- const results = [];
696
- try {
697
- const signatures = await this.connection.getSignaturesForAddress(
698
- MEMO_PROGRAM_ID,
699
- { limit }
700
- );
701
- for (const sigInfo of signatures) {
702
- const tx = await this.connection.getParsedTransaction(
703
- sigInfo.signature,
704
- { maxSupportedTransactionVersion: 0 }
705
- );
706
- if (!tx) continue;
707
- const memo = this.extractMemo(tx);
708
- if (!memo?.startsWith(MEMO_PREFIX)) continue;
709
- const stealthTx = await this.parseStealthTransaction(
710
- tx,
711
- sigInfo.signature,
712
- metaSk,
713
- metaPubkey
714
- );
715
- if (stealthTx) {
716
- results.push(stealthTx);
717
- }
718
- }
719
- } catch (error) {
720
- console.error("Error scanning transactions:", error);
721
- }
722
- return results;
723
- }
724
- /**
725
- * Scans all transactions with ADLSv1 memo prefix
726
- *
727
- * @param metaSk - Recipient's meta secret key
728
- * @param metaPubkey - Recipient's meta public key
729
- * @param since - Scan transactions after this signature
730
- * @returns Array of stealth transactions
731
- */
732
- async scanByMemoPrefix(metaSk, metaPubkey, since) {
426
+ /** Scan for stealth transfers to this recipient */
427
+ async scanForStealthTransfers(metaSk, metaPk, limit = 100) {
428
+ const sigs = await this.connection.getSignaturesForAddress(
429
+ ADELOS_CONFIG.MEMO_PROGRAM_ID,
430
+ { limit }
431
+ );
733
432
  const results = [];
734
- try {
735
- const signatures = await this.connection.getSignaturesForAddress(
736
- MEMO_PROGRAM_ID,
737
- { limit: 1e3, until: since }
738
- );
739
- for (const sigInfo of signatures) {
740
- const tx = await this.connection.getParsedTransaction(
741
- sigInfo.signature,
742
- { maxSupportedTransactionVersion: 0 }
743
- );
744
- if (!tx) continue;
745
- const memo = this.extractMemo(tx);
746
- if (!memo?.startsWith(MEMO_PREFIX)) continue;
747
- const stealthTx = await this.parseStealthTransaction(
748
- tx,
749
- sigInfo.signature,
750
- metaSk,
751
- metaPubkey
752
- );
753
- if (stealthTx) {
754
- results.push(stealthTx);
755
- }
433
+ for (const s of sigs) {
434
+ const tx = await this.connection.getParsedTransaction(s.signature, {
435
+ maxSupportedTransactionVersion: 0
436
+ });
437
+ if (!tx) continue;
438
+ const memo = this.extractMemo(tx);
439
+ if (!memo?.startsWith(ADELOS_CONFIG.MEMO_PREFIX)) continue;
440
+ const detected = this.attemptDecryption(tx, metaSk, metaPk);
441
+ if (detected) {
442
+ results.push({
443
+ signature: s.signature,
444
+ blockTime: tx.blockTime ?? null,
445
+ stealthAddress: detected.stealthAddress,
446
+ amount: detected.amount
447
+ });
756
448
  }
757
- } catch (error) {
758
- console.error("Error scanning by memo:", error);
759
449
  }
760
450
  return results;
761
451
  }
762
- /**
763
- * Extracts memo from a parsed transaction
764
- */
765
- extractMemo(tx) {
766
- const memoProgram = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr";
767
- for (const ix of tx.transaction.message.instructions) {
768
- if ("programId" in ix && ix.programId.toBase58() === memoProgram) {
769
- if ("parsed" in ix && typeof ix.parsed === "string") {
770
- return ix.parsed;
771
- }
772
- }
773
- }
774
- if (tx.meta?.innerInstructions) {
775
- for (const inner of tx.meta.innerInstructions) {
776
- for (const ix of inner.instructions) {
777
- if ("programId" in ix && ix.programId.toBase58() === memoProgram) {
778
- if ("parsed" in ix && typeof ix.parsed === "string") {
779
- return ix.parsed;
780
- }
781
- }
782
- }
783
- }
784
- }
785
- return null;
786
- }
787
- /**
788
- * Parses a transaction to extract stealth transfer info using Trial Decryption
789
- *
790
- * Trial Decryption: First compute expected stealth address, then find if it exists in tx accounts
791
- */
792
- async parseStealthTransaction(tx, signature, metaSk, metaPubkey) {
452
+ /** Trial Decryption: Check if transaction is for this recipient */
453
+ attemptDecryption(tx, metaSk, metaPk) {
793
454
  const memo = this.extractMemo(tx);
794
- if (!memo) return null;
795
- const ephemeralPubkey = parseStealthMemo(memo);
796
- if (!ephemeralPubkey) return null;
797
- let expectedStealthHex;
798
- try {
799
- const sharedSecret = await computeSharedSecretAsRecipient(
800
- metaSk,
801
- ephemeralPubkey
802
- );
803
- const expectedStealth = deriveStealthPubkey(metaPubkey, sharedSecret);
804
- expectedStealthHex = bytesToHex(expectedStealth);
805
- } catch (error) {
806
- console.warn("Error computing expected stealth:", error);
807
- return null;
808
- }
455
+ const ephemeralPk = parseStealthMemo(memo || "");
456
+ if (!ephemeralPk) return null;
457
+ const secret = computeSharedSecretAsRecipient(metaSk, ephemeralPk);
458
+ const expectedStealthHex = bytesToHex(deriveStealthPubkey(metaPk, secret));
809
459
  const accounts = tx.transaction.message.accountKeys;
460
+ const idx = accounts.findIndex(
461
+ (a) => bytesToHex(a.pubkey.toBytes()) === expectedStealthHex
462
+ );
463
+ if (idx === -1) return null;
810
464
  const preBalances = tx.meta?.preBalances || [];
811
465
  const postBalances = tx.meta?.postBalances || [];
812
- let stealthIndex = -1;
813
- for (let i = 0; i < accounts.length; i++) {
814
- const accountHex = bytesToHex(accounts[i].pubkey.toBytes());
815
- if (accountHex === expectedStealthHex) {
816
- stealthIndex = i;
817
- break;
818
- }
819
- }
820
- if (stealthIndex === -1) return null;
821
- const stealthAddress = accounts[stealthIndex].pubkey;
822
- const change = (postBalances[stealthIndex] || 0) - (preBalances[stealthIndex] || 0);
823
- const amount = BigInt(Math.max(change, 0));
824
- let sender = null;
825
- for (let i = 0; i < accounts.length; i++) {
826
- const accChange = (postBalances[i] || 0) - (preBalances[i] || 0);
827
- if (accChange < 0) {
828
- sender = accounts[i].pubkey;
829
- break;
830
- }
831
- }
466
+ const change = (postBalances[idx] || 0) - (preBalances[idx] || 0);
832
467
  return {
833
- signature,
834
- blockTime: tx.blockTime ?? null,
835
- slot: tx.slot,
836
- sender,
837
- stealthAddress,
838
- amount,
839
- ephemeralPubkey,
840
- isForMe: true
841
- // If we reach here, it's definitely for us
468
+ stealthAddress: accounts[idx].pubkey,
469
+ amount: BigInt(Math.max(change, 0))
842
470
  };
843
471
  }
844
- /**
845
- * Starts continuous scanning for stealth transfers
846
- *
847
- * @param metaSk - Recipient's meta secret key
848
- * @param metaPubkey - Recipient's meta public key
849
- */
850
- startScanning(metaSk, metaPubkey) {
851
- if (this.isScanning) return;
852
- this.isScanning = true;
853
- let lastSignature;
854
- const scanInterval = this.config.scanInterval || 1e4;
855
- this.scanIntervalId = setInterval(async () => {
856
- try {
857
- const txs = await this.scanByMemoPrefix(
858
- metaSk,
859
- metaPubkey,
860
- lastSignature
861
- );
862
- for (const tx of txs) {
863
- if (tx.isForMe && this.onTransaction) {
864
- this.onTransaction(tx);
865
- }
866
- }
867
- if (txs.length > 0) {
868
- lastSignature = txs[0].signature;
869
- }
870
- } catch (error) {
871
- console.error("Scan error:", error);
872
- }
873
- }, scanInterval);
874
- }
875
- /**
876
- * Stops continuous scanning
877
- */
878
- stopScanning() {
879
- this.isScanning = false;
880
- if (this.scanIntervalId) {
881
- clearInterval(this.scanIntervalId);
882
- this.scanIntervalId = null;
883
- }
884
- }
885
- /**
886
- * Processes a Helius webhook payload
887
- *
888
- * @param payload - Webhook payload from Helius
889
- * @param metaSk - Recipient's meta secret key
890
- * @param metaPubkey - Recipient's meta public key
891
- * @returns Stealth transaction if detected and for this recipient
892
- */
893
- async processWebhook(payload, metaSk, metaPubkey) {
894
- let memo = null;
895
- const memoProgramStr = MEMO_PROGRAM_ID.toBase58();
896
- for (const ix of payload.instructions) {
897
- if (ix.programId === memoProgramStr) {
898
- memo = Buffer.from(ix.data, "base64").toString("utf-8");
899
- break;
900
- }
901
- }
902
- if (!memo?.startsWith(MEMO_PREFIX)) return null;
903
- const ephemeralPubkey = parseStealthMemo(memo);
904
- if (!ephemeralPubkey) return null;
905
- let stealthAddress = null;
906
- let amount = BigInt(0);
907
- for (const acc of payload.accountData) {
908
- if (acc.nativeBalanceChange > 0) {
909
- stealthAddress = new import_web34.PublicKey(acc.account);
910
- amount = BigInt(acc.nativeBalanceChange);
911
- break;
912
- }
913
- }
914
- if (!stealthAddress) return null;
915
- let isForMe = false;
916
- try {
917
- const sharedSecret = await computeSharedSecretAsRecipient(
918
- metaSk,
919
- ephemeralPubkey
920
- );
921
- const expectedStealth = deriveStealthPubkey(metaPubkey, sharedSecret);
922
- isForMe = bytesToHex(expectedStealth) === bytesToHex(stealthAddress.toBytes());
923
- } catch (error) {
924
- console.warn("Error checking webhook tx:", error);
925
- }
926
- if (!isForMe) return null;
927
- return {
928
- signature: payload.signature,
929
- blockTime: payload.timestamp,
930
- slot: payload.slot,
931
- sender: null,
932
- stealthAddress,
933
- amount,
934
- ephemeralPubkey,
935
- isForMe: true
936
- };
937
- }
938
- /**
939
- * Sets up Helius webhook for real-time notifications
940
- *
941
- * @param webhookUrl - Your server's webhook endpoint
942
- * @returns Webhook ID
943
- */
944
- async setupHeliusWebhook(webhookUrl) {
945
- if (!this.config.heliusApiKey) {
946
- console.warn("Helius API key not configured");
947
- return null;
948
- }
949
- try {
950
- const response = await fetch(
951
- `https://api.helius.xyz/v0/webhooks?api-key=${this.config.heliusApiKey}`,
952
- {
953
- method: "POST",
954
- headers: { "Content-Type": "application/json" },
955
- body: JSON.stringify({
956
- webhookURL: webhookUrl,
957
- transactionTypes: ["TRANSFER"],
958
- accountAddresses: [
959
- MEMO_PROGRAM_ID.toBase58()
960
- ],
961
- webhookType: "enhanced"
962
- })
963
- }
964
- );
965
- const data = await response.json();
966
- return data.webhookID || null;
967
- } catch (error) {
968
- console.error("Failed to setup Helius webhook:", error);
969
- return null;
970
- }
472
+ extractMemo(tx) {
473
+ const ix = tx.transaction.message.instructions.find(
474
+ (i) => i.programId.equals(ADELOS_CONFIG.MEMO_PROGRAM_ID)
475
+ );
476
+ return ix?.parsed || null;
971
477
  }
972
478
  };
973
479
  function createIndexer(config) {
974
- return AdelosIndexer.create(config);
480
+ return new AdelosIndexer(config);
975
481
  }
976
482
 
977
483
  // src/index.ts
978
484
  var AdelosSDK = class {
979
485
  constructor(options = {}) {
980
- this.cluster = options.cluster ?? "devnet";
981
- const rpcUrl = options.rpcUrl ?? RPC_URLS[this.cluster];
982
- this.connection = new import_web35.Connection(rpcUrl, "confirmed");
983
- this.programId = PROGRAM_IDS[this.cluster];
984
- this.heliusApiKey = options.heliusApiKey;
486
+ const rpcUrl = options.rpcUrl ?? ADELOS_CONFIG.RPC_URL;
487
+ this.connection = new import_web34.Connection(rpcUrl, "confirmed");
488
+ this.programId = ADELOS_CONFIG.PROGRAM_ID;
985
489
  }
986
- /**
987
- * Derives the registry PDA for an owner
988
- */
490
+ // --- Registry Operations ---
989
491
  deriveRegistryAddress(owner) {
990
492
  return deriveRegistryPda(owner, this.programId);
991
493
  }
992
- /**
993
- * Fetches a registry account by owner
994
- */
995
494
  async getRegistry(owner) {
996
495
  const [address] = this.deriveRegistryAddress(owner);
997
496
  const accountInfo = await this.connection.getAccountInfo(address);
998
497
  if (!accountInfo) {
999
- return {
1000
- address,
1001
- exists: false,
1002
- account: null
1003
- };
498
+ return { address, exists: false, account: null };
1004
499
  }
1005
500
  const data = accountInfo.data.slice(8);
1006
501
  const account = {
1007
- owner: new import_web35.PublicKey(data.slice(0, 32)),
502
+ owner: new import_web34.PublicKey(data.slice(0, 32)),
1008
503
  metaPubkey: new Uint8Array(data.slice(32, 64)),
1009
504
  bump: data[64]
1010
505
  };
1011
506
  return { address, exists: true, account };
1012
507
  }
1013
- /**
1014
- * Gets the meta pubkey for an owner
1015
- * @returns The meta pubkey as Uint8Array, or null if not registered
1016
- */
1017
508
  async getMetaPubkey(owner) {
1018
509
  const registry = await this.getRegistry(owner);
1019
510
  return registry.exists ? registry.account.metaPubkey : null;
1020
511
  }
1021
- /**
1022
- * Gets the meta pubkey as hex string
1023
- */
1024
- async getMetaPubkeyHex(owner) {
1025
- const metaPubkey = await this.getMetaPubkey(owner);
1026
- return metaPubkey ? bytesToHex(metaPubkey) : null;
1027
- }
1028
- /**
1029
- * Checks if an owner has a registered identity
1030
- */
1031
512
  async isRegistered(owner) {
1032
513
  const registry = await this.getRegistry(owner);
1033
514
  return registry.exists;
1034
515
  }
1035
- /**
1036
- * Creates a register identity instruction
1037
- */
516
+ // --- Instruction Builders ---
1038
517
  createRegisterInstruction(owner, metaPubkey) {
1039
518
  if (!isValidMetaPubkey(metaPubkey)) {
1040
519
  throw new Error("Invalid meta pubkey: must be 32 bytes and not all zeros");
@@ -1044,19 +523,16 @@ var AdelosSDK = class {
1044
523
  Buffer.from(getDiscriminator("register_identity")),
1045
524
  Buffer.from(metaPubkey)
1046
525
  ]);
1047
- return new import_web35.TransactionInstruction({
526
+ return new import_web34.TransactionInstruction({
1048
527
  keys: [
1049
528
  { pubkey: owner, isSigner: true, isWritable: true },
1050
529
  { pubkey: registryPda, isSigner: false, isWritable: true },
1051
- { pubkey: import_web35.SystemProgram.programId, isSigner: false, isWritable: false }
530
+ { pubkey: import_web34.SystemProgram.programId, isSigner: false, isWritable: false }
1052
531
  ],
1053
532
  programId: this.programId,
1054
533
  data
1055
534
  });
1056
535
  }
1057
- /**
1058
- * Creates an update identity instruction
1059
- */
1060
536
  createUpdateInstruction(owner, newMetaPubkey) {
1061
537
  if (!isValidMetaPubkey(newMetaPubkey)) {
1062
538
  throw new Error("Invalid meta pubkey: must be 32 bytes and not all zeros");
@@ -1066,22 +542,20 @@ var AdelosSDK = class {
1066
542
  Buffer.from(getDiscriminator("update_identity")),
1067
543
  Buffer.from(newMetaPubkey)
1068
544
  ]);
1069
- return new import_web35.TransactionInstruction({
545
+ return new import_web34.TransactionInstruction({
1070
546
  keys: [
1071
- { pubkey: owner, isSigner: true, isWritable: false },
1072
- { pubkey: registryPda, isSigner: false, isWritable: true }
547
+ { pubkey: owner, isSigner: true, isWritable: true },
548
+ { pubkey: registryPda, isSigner: false, isWritable: true },
549
+ { pubkey: import_web34.SystemProgram.programId, isSigner: false, isWritable: false }
1073
550
  ],
1074
551
  programId: this.programId,
1075
552
  data
1076
553
  });
1077
554
  }
1078
- /**
1079
- * Creates a close registry instruction
1080
- */
1081
555
  createCloseInstruction(owner) {
1082
556
  const [registryPda] = this.deriveRegistryAddress(owner);
1083
557
  const data = Buffer.from(getDiscriminator("close_registry"));
1084
- return new import_web35.TransactionInstruction({
558
+ return new import_web34.TransactionInstruction({
1085
559
  keys: [
1086
560
  { pubkey: owner, isSigner: true, isWritable: true },
1087
561
  { pubkey: registryPda, isSigner: false, isWritable: true }
@@ -1090,117 +564,57 @@ var AdelosSDK = class {
1090
564
  data
1091
565
  });
1092
566
  }
1093
- /**
1094
- * Creates a register transaction (unsigned) - Legacy format
1095
- */
1096
- async createRegisterTransaction(owner, metaPubkey) {
1097
- const instruction = this.createRegisterInstruction(owner, metaPubkey);
1098
- const transaction = new import_web35.Transaction().add(instruction);
1099
- transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
1100
- transaction.feePayer = owner;
1101
- return transaction;
1102
- }
1103
- /**
1104
- * Creates a register transaction (unsigned) - Versioned format (v0)
1105
- * Recommended for modern Solana applications
1106
- */
1107
- async createRegisterTransactionV0(owner, metaPubkey) {
1108
- const instruction = this.createRegisterInstruction(owner, metaPubkey);
567
+ // --- Transaction Builders ---
568
+ async buildTransaction(payer, instructions, version = "v0") {
1109
569
  const { blockhash } = await this.connection.getLatestBlockhash();
1110
- const messageV0 = new import_web35.TransactionMessage({
1111
- payerKey: owner,
1112
- recentBlockhash: blockhash,
1113
- instructions: [instruction]
1114
- }).compileToV0Message();
1115
- return new import_web35.VersionedTransaction(messageV0);
1116
- }
1117
- /**
1118
- * Creates an update transaction (unsigned) - Legacy format
1119
- */
1120
- async createUpdateTransaction(owner, newMetaPubkey) {
1121
- const instruction = this.createUpdateInstruction(owner, newMetaPubkey);
1122
- const transaction = new import_web35.Transaction().add(instruction);
1123
- transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
1124
- transaction.feePayer = owner;
1125
- return transaction;
1126
- }
1127
- /**
1128
- * Creates an update transaction (unsigned) - Versioned format (v0)
1129
- */
1130
- async createUpdateTransactionV0(owner, newMetaPubkey) {
1131
- const instruction = this.createUpdateInstruction(owner, newMetaPubkey);
1132
- const { blockhash } = await this.connection.getLatestBlockhash();
1133
- const messageV0 = new import_web35.TransactionMessage({
1134
- payerKey: owner,
570
+ if (version === "legacy") {
571
+ const tx = new import_web34.Transaction().add(...instructions);
572
+ tx.recentBlockhash = blockhash;
573
+ tx.feePayer = payer;
574
+ return tx;
575
+ }
576
+ const message = new import_web34.TransactionMessage({
577
+ payerKey: payer,
1135
578
  recentBlockhash: blockhash,
1136
- instructions: [instruction]
579
+ instructions
1137
580
  }).compileToV0Message();
1138
- return new import_web35.VersionedTransaction(messageV0);
581
+ return new import_web34.VersionedTransaction(message);
1139
582
  }
1140
- /**
1141
- * Creates a close transaction (unsigned) - Legacy format
1142
- */
1143
- async createCloseTransaction(owner) {
1144
- const instruction = this.createCloseInstruction(owner);
1145
- const transaction = new import_web35.Transaction().add(instruction);
1146
- transaction.recentBlockhash = (await this.connection.getLatestBlockhash()).blockhash;
1147
- transaction.feePayer = owner;
1148
- return transaction;
583
+ async createRegisterTransaction(owner, metaPubkey) {
584
+ const ix = this.createRegisterInstruction(owner, metaPubkey);
585
+ return this.buildTransaction(owner, [ix], "legacy");
1149
586
  }
1150
- /**
1151
- * Creates a close transaction (unsigned) - Versioned format (v0)
1152
- */
1153
- async createCloseTransactionV0(owner) {
1154
- const instruction = this.createCloseInstruction(owner);
1155
- const { blockhash } = await this.connection.getLatestBlockhash();
1156
- const messageV0 = new import_web35.TransactionMessage({
1157
- payerKey: owner,
1158
- recentBlockhash: blockhash,
1159
- instructions: [instruction]
1160
- }).compileToV0Message();
1161
- return new import_web35.VersionedTransaction(messageV0);
587
+ async createRegisterTransactionV0(owner, metaPubkey) {
588
+ const ix = this.createRegisterInstruction(owner, metaPubkey);
589
+ return this.buildTransaction(owner, [ix], "v0");
1162
590
  }
1163
- /**
1164
- * Sends a signed legacy transaction and confirms it
1165
- */
1166
- async sendAndConfirm(signedTransaction) {
1167
- const signature = await this.connection.sendRawTransaction(
1168
- signedTransaction.serialize()
1169
- );
1170
- await this.connection.confirmTransaction(signature, "confirmed");
1171
- return signature;
591
+ async sendAndConfirm(signedTx) {
592
+ const sig = await this.connection.sendRawTransaction(signedTx.serialize());
593
+ await this.connection.confirmTransaction(sig, "confirmed");
594
+ return sig;
1172
595
  }
1173
- /**
1174
- * Sends a signed versioned transaction and confirms it
1175
- */
1176
- async sendAndConfirmV0(signedTransaction) {
1177
- const signature = await this.connection.sendRawTransaction(
1178
- signedTransaction.serialize()
1179
- );
1180
- await this.connection.confirmTransaction(signature, "confirmed");
1181
- return signature;
596
+ async sendAndConfirmV0(signedTx) {
597
+ const sig = await this.connection.sendRawTransaction(signedTx.serialize());
598
+ await this.connection.confirmTransaction(sig, "confirmed");
599
+ return sig;
1182
600
  }
1183
601
  };
1184
602
  // Annotate the CommonJS export names for ESM import in node:
1185
603
  0 && (module.exports = {
604
+ ADELOS_CONFIG,
1186
605
  AdelosIndexer,
1187
606
  AdelosSDK,
1188
607
  IDL,
1189
- LIGHT_PROGRAM_IDS,
1190
- LightClient,
1191
608
  MEMO_PREFIX,
1192
609
  MEMO_PROGRAM_ID,
1193
610
  PROGRAM_ID,
1194
- PROGRAM_IDS,
1195
- REGISTRY_ACCOUNT_SIZE,
1196
611
  REGISTRY_SEED,
1197
- RPC_URLS,
612
+ RPC_URL,
1198
613
  STEALTH_DOMAIN,
1199
614
  bytesToHex,
1200
615
  computeSharedSecret,
1201
616
  computeSharedSecretAsRecipient,
1202
617
  createIndexer,
1203
- createLightClient,
1204
618
  deriveRegistryPda,
1205
619
  deriveStealthPubkey,
1206
620
  generateEphemeralKeypair,
@@ -1208,7 +622,6 @@ var AdelosSDK = class {
1208
622
  generateStealthMemo,
1209
623
  getDiscriminator,
1210
624
  hexToBytes,
1211
- isStealthTransactionForMe,
1212
625
  isValidMetaPubkey,
1213
626
  parseStealthMemo,
1214
627
  recoverStealthSecretKey