@adelos/sdk 0.1.2 → 0.1.3

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,51 @@ 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 point = ed.ExtendedPoint.fromHex(bytesToHex(ephemeralPk));
374
+ const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(metaSk)), ed.CURVE.n);
375
+ return (0, import_sha256.sha256)(point.multiply(scalar).toRawBytes());
376
+ }
377
+ function deriveStealthPubkey(metaPk, sharedSecret) {
378
+ const domain = encoder.encode(ADELOS_CONFIG.STEALTH_DOMAIN);
379
+ const scalarBytes = (0, import_sha256.sha256)(new Uint8Array([...sharedSecret, ...domain]));
380
+ const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(scalarBytes)), ed.CURVE.n);
381
+ const metaPoint = ed.ExtendedPoint.fromHex(bytesToHex(metaPk));
382
+ const stealthPoint = metaPoint.add(ed.ExtendedPoint.BASE.multiply(scalar));
392
383
  return stealthPoint.toRawBytes();
393
384
  }
394
385
  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
- );
386
+ const domain = encoder.encode(ADELOS_CONFIG.STEALTH_DOMAIN);
387
+ const scalarBytes = (0, import_sha256.sha256)(new Uint8Array([...sharedSecret, ...domain]));
388
+ const scalar = ed.etc.mod(BigInt("0x" + bytesToHex(scalarBytes)), ed.CURVE.n);
389
+ const metaScalar = ed.etc.mod(BigInt("0x" + bytesToHex(metaSk)), ed.CURVE.n);
406
390
  const stealthScalar = ed.etc.mod(metaScalar + scalar, ed.CURVE.n);
407
391
  const hex = stealthScalar.toString(16).padStart(64, "0");
408
392
  return hexToBytes(hex);
409
393
  }
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
394
  function generateStealthMemo(ephemeralPubkey) {
418
- const pubkeyHex = bytesToHex(ephemeralPubkey);
419
- return `${MEMO_PREFIX}${pubkeyHex}`;
395
+ return `${ADELOS_CONFIG.MEMO_PREFIX}${bytesToHex(ephemeralPubkey)}`;
420
396
  }
421
397
  function parseStealthMemo(memo) {
422
- if (!memo.startsWith(MEMO_PREFIX)) return null;
423
- const pubkeyHex = memo.slice(MEMO_PREFIX.length);
398
+ if (!memo.startsWith(ADELOS_CONFIG.MEMO_PREFIX)) return null;
399
+ const pubkeyHex = memo.slice(ADELOS_CONFIG.MEMO_PREFIX.length);
424
400
  if (pubkeyHex.length !== 64) return null;
425
401
  try {
426
402
  return hexToBytes(pubkeyHex);
@@ -428,613 +404,115 @@ function parseStealthMemo(memo) {
428
404
  return null;
429
405
  }
430
406
  }
431
- async function generateStealthAddress(recipientMetaPubkey) {
407
+ function generateStealthAddress(recipientMetaPk) {
432
408
  const ephemeralKeypair = generateEphemeralKeypair();
433
- const sharedSecret = await computeSharedSecret(
434
- ephemeralKeypair.secretKey,
435
- recipientMetaPubkey
436
- );
437
- const stealthPubkey = deriveStealthPubkey(recipientMetaPubkey, sharedSecret);
409
+ const sharedSecret = computeSharedSecret(ephemeralKeypair.secretKey, recipientMetaPk);
410
+ const stealthPubkey = deriveStealthPubkey(recipientMetaPk, sharedSecret);
438
411
  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);
412
+ return { stealthPubkey, ephemeralKeypair, sharedSecret, memo };
665
413
  }
666
414
 
667
415
  // src/indexer.ts
668
- var import_web34 = require("@solana/web3.js");
416
+ var import_web33 = require("@solana/web3.js");
669
417
  var AdelosIndexer = class _AdelosIndexer {
670
418
  constructor(config) {
671
- this.isScanning = false;
672
- this.scanIntervalId = null;
673
- this.config = config;
674
- this.connection = new import_web34.Connection(config.rpcUrl, "confirmed");
419
+ this.connection = new import_web33.Connection(config.rpcUrl, "confirmed");
420
+ this.heliusApiKey = config.heliusApiKey;
675
421
  }
676
- /**
677
- * Creates an indexer instance
678
- */
679
422
  static create(config) {
680
423
  return new _AdelosIndexer(config);
681
424
  }
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) {
425
+ /** Scan for stealth transfers to this recipient */
426
+ async scanForStealthTransfers(metaSk, metaPk, limit = 100) {
427
+ const sigs = await this.connection.getSignaturesForAddress(
428
+ ADELOS_CONFIG.MEMO_PROGRAM_ID,
429
+ { limit }
430
+ );
733
431
  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
- }
432
+ for (const s of sigs) {
433
+ const tx = await this.connection.getParsedTransaction(s.signature, {
434
+ maxSupportedTransactionVersion: 0
435
+ });
436
+ if (!tx) continue;
437
+ const memo = this.extractMemo(tx);
438
+ if (!memo?.startsWith(ADELOS_CONFIG.MEMO_PREFIX)) continue;
439
+ const detected = this.attemptDecryption(tx, metaSk, metaPk);
440
+ if (detected) {
441
+ results.push({
442
+ signature: s.signature,
443
+ blockTime: tx.blockTime ?? null,
444
+ stealthAddress: detected.stealthAddress,
445
+ amount: detected.amount
446
+ });
756
447
  }
757
- } catch (error) {
758
- console.error("Error scanning by memo:", error);
759
448
  }
760
449
  return results;
761
450
  }
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) {
451
+ /** Trial Decryption: Check if transaction is for this recipient */
452
+ attemptDecryption(tx, metaSk, metaPk) {
793
453
  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
- }
454
+ const ephemeralPk = parseStealthMemo(memo || "");
455
+ if (!ephemeralPk) return null;
456
+ const secret = computeSharedSecretAsRecipient(metaSk, ephemeralPk);
457
+ const expectedStealthHex = bytesToHex(deriveStealthPubkey(metaPk, secret));
809
458
  const accounts = tx.transaction.message.accountKeys;
459
+ const idx = accounts.findIndex(
460
+ (a) => bytesToHex(a.pubkey.toBytes()) === expectedStealthHex
461
+ );
462
+ if (idx === -1) return null;
810
463
  const preBalances = tx.meta?.preBalances || [];
811
464
  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
- }
832
- 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
842
- };
843
- }
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;
465
+ const change = (postBalances[idx] || 0) - (preBalances[idx] || 0);
927
466
  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
467
+ stealthAddress: accounts[idx].pubkey,
468
+ amount: BigInt(Math.max(change, 0))
936
469
  };
937
470
  }
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
- }
471
+ extractMemo(tx) {
472
+ const ix = tx.transaction.message.instructions.find(
473
+ (i) => i.programId.equals(ADELOS_CONFIG.MEMO_PROGRAM_ID)
474
+ );
475
+ return ix?.parsed || null;
971
476
  }
972
477
  };
973
478
  function createIndexer(config) {
974
- return AdelosIndexer.create(config);
479
+ return new AdelosIndexer(config);
975
480
  }
976
481
 
977
482
  // src/index.ts
978
483
  var AdelosSDK = class {
979
484
  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;
485
+ const rpcUrl = options.rpcUrl ?? ADELOS_CONFIG.RPC_URL;
486
+ this.connection = new import_web34.Connection(rpcUrl, "confirmed");
487
+ this.programId = ADELOS_CONFIG.PROGRAM_ID;
985
488
  }
986
- /**
987
- * Derives the registry PDA for an owner
988
- */
489
+ // --- Registry Operations ---
989
490
  deriveRegistryAddress(owner) {
990
491
  return deriveRegistryPda(owner, this.programId);
991
492
  }
992
- /**
993
- * Fetches a registry account by owner
994
- */
995
493
  async getRegistry(owner) {
996
494
  const [address] = this.deriveRegistryAddress(owner);
997
495
  const accountInfo = await this.connection.getAccountInfo(address);
998
496
  if (!accountInfo) {
999
- return {
1000
- address,
1001
- exists: false,
1002
- account: null
1003
- };
497
+ return { address, exists: false, account: null };
1004
498
  }
1005
499
  const data = accountInfo.data.slice(8);
1006
500
  const account = {
1007
- owner: new import_web35.PublicKey(data.slice(0, 32)),
501
+ owner: new import_web34.PublicKey(data.slice(0, 32)),
1008
502
  metaPubkey: new Uint8Array(data.slice(32, 64)),
1009
503
  bump: data[64]
1010
504
  };
1011
505
  return { address, exists: true, account };
1012
506
  }
1013
- /**
1014
- * Gets the meta pubkey for an owner
1015
- * @returns The meta pubkey as Uint8Array, or null if not registered
1016
- */
1017
507
  async getMetaPubkey(owner) {
1018
508
  const registry = await this.getRegistry(owner);
1019
509
  return registry.exists ? registry.account.metaPubkey : null;
1020
510
  }
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
511
  async isRegistered(owner) {
1032
512
  const registry = await this.getRegistry(owner);
1033
513
  return registry.exists;
1034
514
  }
1035
- /**
1036
- * Creates a register identity instruction
1037
- */
515
+ // --- Instruction Builders ---
1038
516
  createRegisterInstruction(owner, metaPubkey) {
1039
517
  if (!isValidMetaPubkey(metaPubkey)) {
1040
518
  throw new Error("Invalid meta pubkey: must be 32 bytes and not all zeros");
@@ -1044,19 +522,16 @@ var AdelosSDK = class {
1044
522
  Buffer.from(getDiscriminator("register_identity")),
1045
523
  Buffer.from(metaPubkey)
1046
524
  ]);
1047
- return new import_web35.TransactionInstruction({
525
+ return new import_web34.TransactionInstruction({
1048
526
  keys: [
1049
527
  { pubkey: owner, isSigner: true, isWritable: true },
1050
528
  { pubkey: registryPda, isSigner: false, isWritable: true },
1051
- { pubkey: import_web35.SystemProgram.programId, isSigner: false, isWritable: false }
529
+ { pubkey: import_web34.SystemProgram.programId, isSigner: false, isWritable: false }
1052
530
  ],
1053
531
  programId: this.programId,
1054
532
  data
1055
533
  });
1056
534
  }
1057
- /**
1058
- * Creates an update identity instruction
1059
- */
1060
535
  createUpdateInstruction(owner, newMetaPubkey) {
1061
536
  if (!isValidMetaPubkey(newMetaPubkey)) {
1062
537
  throw new Error("Invalid meta pubkey: must be 32 bytes and not all zeros");
@@ -1066,22 +541,20 @@ var AdelosSDK = class {
1066
541
  Buffer.from(getDiscriminator("update_identity")),
1067
542
  Buffer.from(newMetaPubkey)
1068
543
  ]);
1069
- return new import_web35.TransactionInstruction({
544
+ return new import_web34.TransactionInstruction({
1070
545
  keys: [
1071
- { pubkey: owner, isSigner: true, isWritable: false },
1072
- { pubkey: registryPda, isSigner: false, isWritable: true }
546
+ { pubkey: owner, isSigner: true, isWritable: true },
547
+ { pubkey: registryPda, isSigner: false, isWritable: true },
548
+ { pubkey: import_web34.SystemProgram.programId, isSigner: false, isWritable: false }
1073
549
  ],
1074
550
  programId: this.programId,
1075
551
  data
1076
552
  });
1077
553
  }
1078
- /**
1079
- * Creates a close registry instruction
1080
- */
1081
554
  createCloseInstruction(owner) {
1082
555
  const [registryPda] = this.deriveRegistryAddress(owner);
1083
556
  const data = Buffer.from(getDiscriminator("close_registry"));
1084
- return new import_web35.TransactionInstruction({
557
+ return new import_web34.TransactionInstruction({
1085
558
  keys: [
1086
559
  { pubkey: owner, isSigner: true, isWritable: true },
1087
560
  { pubkey: registryPda, isSigner: false, isWritable: true }
@@ -1090,117 +563,57 @@ var AdelosSDK = class {
1090
563
  data
1091
564
  });
1092
565
  }
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);
1109
- 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);
566
+ // --- Transaction Builders ---
567
+ async buildTransaction(payer, instructions, version = "v0") {
1132
568
  const { blockhash } = await this.connection.getLatestBlockhash();
1133
- const messageV0 = new import_web35.TransactionMessage({
1134
- payerKey: owner,
569
+ if (version === "legacy") {
570
+ const tx = new import_web34.Transaction().add(...instructions);
571
+ tx.recentBlockhash = blockhash;
572
+ tx.feePayer = payer;
573
+ return tx;
574
+ }
575
+ const message = new import_web34.TransactionMessage({
576
+ payerKey: payer,
1135
577
  recentBlockhash: blockhash,
1136
- instructions: [instruction]
578
+ instructions
1137
579
  }).compileToV0Message();
1138
- return new import_web35.VersionedTransaction(messageV0);
580
+ return new import_web34.VersionedTransaction(message);
1139
581
  }
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;
582
+ async createRegisterTransaction(owner, metaPubkey) {
583
+ const ix = this.createRegisterInstruction(owner, metaPubkey);
584
+ return this.buildTransaction(owner, [ix], "legacy");
1149
585
  }
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);
586
+ async createRegisterTransactionV0(owner, metaPubkey) {
587
+ const ix = this.createRegisterInstruction(owner, metaPubkey);
588
+ return this.buildTransaction(owner, [ix], "v0");
1162
589
  }
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;
590
+ async sendAndConfirm(signedTx) {
591
+ const sig = await this.connection.sendRawTransaction(signedTx.serialize());
592
+ await this.connection.confirmTransaction(sig, "confirmed");
593
+ return sig;
1172
594
  }
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;
595
+ async sendAndConfirmV0(signedTx) {
596
+ const sig = await this.connection.sendRawTransaction(signedTx.serialize());
597
+ await this.connection.confirmTransaction(sig, "confirmed");
598
+ return sig;
1182
599
  }
1183
600
  };
1184
601
  // Annotate the CommonJS export names for ESM import in node:
1185
602
  0 && (module.exports = {
603
+ ADELOS_CONFIG,
1186
604
  AdelosIndexer,
1187
605
  AdelosSDK,
1188
606
  IDL,
1189
- LIGHT_PROGRAM_IDS,
1190
- LightClient,
1191
607
  MEMO_PREFIX,
1192
608
  MEMO_PROGRAM_ID,
1193
609
  PROGRAM_ID,
1194
- PROGRAM_IDS,
1195
- REGISTRY_ACCOUNT_SIZE,
1196
610
  REGISTRY_SEED,
1197
- RPC_URLS,
611
+ RPC_URL,
1198
612
  STEALTH_DOMAIN,
1199
613
  bytesToHex,
1200
614
  computeSharedSecret,
1201
615
  computeSharedSecretAsRecipient,
1202
616
  createIndexer,
1203
- createLightClient,
1204
617
  deriveRegistryPda,
1205
618
  deriveStealthPubkey,
1206
619
  generateEphemeralKeypair,
@@ -1208,7 +621,6 @@ var AdelosSDK = class {
1208
621
  generateStealthMemo,
1209
622
  getDiscriminator,
1210
623
  hexToBytes,
1211
- isStealthTransactionForMe,
1212
624
  isValidMetaPubkey,
1213
625
  parseStealthMemo,
1214
626
  recoverStealthSecretKey