@opaquecash/stealth-chain-solana 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +69 -0
  2. package/dist/adapter.d.ts +76 -0
  3. package/dist/adapter.d.ts.map +1 -0
  4. package/dist/adapter.js +114 -0
  5. package/dist/adapter.js.map +1 -0
  6. package/dist/announcer.d.ts +92 -0
  7. package/dist/announcer.d.ts.map +1 -0
  8. package/dist/announcer.js +164 -0
  9. package/dist/announcer.js.map +1 -0
  10. package/dist/bytes.d.ts +35 -0
  11. package/dist/bytes.d.ts.map +1 -0
  12. package/dist/bytes.js +78 -0
  13. package/dist/bytes.js.map +1 -0
  14. package/dist/index.d.ts +24 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +24 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/ons.d.ts +122 -0
  19. package/dist/ons.d.ts.map +1 -0
  20. package/dist/ons.js +167 -0
  21. package/dist/ons.js.map +1 -0
  22. package/dist/programs.d.ts +60 -0
  23. package/dist/programs.d.ts.map +1 -0
  24. package/dist/programs.js +69 -0
  25. package/dist/programs.js.map +1 -0
  26. package/dist/registry.d.ts +39 -0
  27. package/dist/registry.d.ts.map +1 -0
  28. package/dist/registry.js +69 -0
  29. package/dist/registry.js.map +1 -0
  30. package/dist/relay.d.ts +71 -0
  31. package/dist/relay.d.ts.map +1 -0
  32. package/dist/relay.js +102 -0
  33. package/dist/relay.js.map +1 -0
  34. package/dist/sns.d.ts +16 -0
  35. package/dist/sns.d.ts.map +1 -0
  36. package/dist/sns.js +38 -0
  37. package/dist/sns.js.map +1 -0
  38. package/dist/stealth.d.ts +22 -0
  39. package/dist/stealth.d.ts.map +1 -0
  40. package/dist/stealth.js +38 -0
  41. package/dist/stealth.js.map +1 -0
  42. package/dist/sweep.d.ts +41 -0
  43. package/dist/sweep.d.ts.map +1 -0
  44. package/dist/sweep.js +76 -0
  45. package/dist/sweep.js.map +1 -0
  46. package/dist/uab-receiver.d.ts +57 -0
  47. package/dist/uab-receiver.d.ts.map +1 -0
  48. package/dist/uab-receiver.js +121 -0
  49. package/dist/uab-receiver.js.map +1 -0
  50. package/idl/stealth_announcer.json +460 -0
  51. package/package.json +42 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `StealthMetaAddressRegistry` (Solana) — PDA derivation, `register_keys` instruction
3
+ * building, and meta-address resolution. Mirrors the read/write surface of
4
+ * `@opaquecash/stealth-chain` (EVM) so both adapters expose the same shape.
5
+ */
6
+ import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
7
+ import type { Hex } from "@opaquecash/adapter";
8
+ /**
9
+ * Derive the registry entry PDA for `(registrant, schemeId)`:
10
+ * `["stealth_meta", registrant, schemeId_le]`.
11
+ */
12
+ export declare function getRegistryEntryPda(registryProgramId: PublicKey, registrant: PublicKey, schemeId?: bigint): PublicKey;
13
+ /**
14
+ * Build a `register_keys` instruction. The caller (`registrant`) signs and pays; the app's
15
+ * wallet layer submits the resulting transaction.
16
+ */
17
+ export declare function buildRegisterKeysInstruction(params: {
18
+ registryProgramId: PublicKey;
19
+ registrant: PublicKey;
20
+ /** 66-byte stealth meta-address (compressed V || S). */
21
+ stealthMetaAddress: Uint8Array;
22
+ schemeId?: bigint;
23
+ }): TransactionInstruction;
24
+ /** Decode the 66-byte meta-address out of raw `RegistryEntry` account data. */
25
+ export declare function decodeRegistryEntryMetaAddress(data: Uint8Array): Hex | null;
26
+ /**
27
+ * Resolve a registrant's 66-byte stealth meta-address via the registry PDA, or `null` when
28
+ * unregistered.
29
+ */
30
+ export declare function resolveMetaAddress(connection: Connection, params: {
31
+ registryProgramId: PublicKey;
32
+ registrant: PublicKey | string;
33
+ }): Promise<Hex | null>;
34
+ /** Whether `registrant` has a stealth meta-address registered. */
35
+ export declare function isRegistered(connection: Connection, params: {
36
+ registryProgramId: PublicKey;
37
+ registrant: PublicKey | string;
38
+ }): Promise<boolean>;
39
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,UAAU,EACV,SAAS,EAET,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAe/C;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,iBAAiB,EAAE,SAAS,EAC5B,UAAU,EAAE,SAAS,EACrB,QAAQ,GAAE,MAA4B,GACrC,SAAS,CAUX;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE;IACnD,iBAAiB,EAAE,SAAS,CAAC;IAC7B,UAAU,EAAE,SAAS,CAAC;IACtB,wDAAwD;IACxD,kBAAkB,EAAE,UAAU,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,sBAAsB,CAqBzB;AAED,+EAA+E;AAC/E,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,UAAU,GAAG,GAAG,GAAG,IAAI,CAO3E;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE;IAAE,iBAAiB,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,SAAS,GAAG,MAAM,CAAA;CAAE,GACvE,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CASrB;AAED,kEAAkE;AAClE,wBAAsB,YAAY,CAChC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE;IAAE,iBAAiB,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,SAAS,GAAG,MAAM,CAAA;CAAE,GACvE,OAAO,CAAC,OAAO,CAAC,CAGlB"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * `StealthMetaAddressRegistry` (Solana) — PDA derivation, `register_keys` instruction
3
+ * building, and meta-address resolution. Mirrors the read/write surface of
4
+ * `@opaquecash/stealth-chain` (EVM) so both adapters expose the same shape.
5
+ */
6
+ import { PublicKey, SystemProgram, TransactionInstruction, } from "@solana/web3.js";
7
+ import { bytesToHex, concatBytes, u64le, vecU8 } from "./bytes.js";
8
+ import { REGISTER_KEYS_DISCRIMINATOR, REGISTRY_ENTRY_SEED, SCHEME_ID_SECP256K1, } from "./programs.js";
9
+ /** Byte layout of a `RegistryEntry` account before the 66-byte meta-address. */
10
+ const REGISTRY_ENTRY_META_OFFSET = 8 /* discriminator */ + 32 /* registrant pubkey */ + 8 /* scheme_id u64 */ + 4 /* vec len */;
11
+ /** Length of a stealth meta-address (compressed V || compressed S). */
12
+ const META_ADDRESS_LEN = 66;
13
+ /**
14
+ * Derive the registry entry PDA for `(registrant, schemeId)`:
15
+ * `["stealth_meta", registrant, schemeId_le]`.
16
+ */
17
+ export function getRegistryEntryPda(registryProgramId, registrant, schemeId = SCHEME_ID_SECP256K1) {
18
+ const [pda] = PublicKey.findProgramAddressSync([
19
+ new TextEncoder().encode(REGISTRY_ENTRY_SEED),
20
+ registrant.toBuffer(),
21
+ u64le(schemeId),
22
+ ], registryProgramId);
23
+ return pda;
24
+ }
25
+ /**
26
+ * Build a `register_keys` instruction. The caller (`registrant`) signs and pays; the app's
27
+ * wallet layer submits the resulting transaction.
28
+ */
29
+ export function buildRegisterKeysInstruction(params) {
30
+ const schemeId = params.schemeId ?? SCHEME_ID_SECP256K1;
31
+ const data = concatBytes(REGISTER_KEYS_DISCRIMINATOR, u64le(schemeId), vecU8(params.stealthMetaAddress));
32
+ const registryEntryPda = getRegistryEntryPda(params.registryProgramId, params.registrant, schemeId);
33
+ return new TransactionInstruction({
34
+ programId: params.registryProgramId,
35
+ keys: [
36
+ { pubkey: registryEntryPda, isSigner: false, isWritable: true },
37
+ { pubkey: params.registrant, isSigner: true, isWritable: true },
38
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
39
+ ],
40
+ data: Buffer.from(data),
41
+ });
42
+ }
43
+ /** Decode the 66-byte meta-address out of raw `RegistryEntry` account data. */
44
+ export function decodeRegistryEntryMetaAddress(data) {
45
+ if (data.length < REGISTRY_ENTRY_META_OFFSET + META_ADDRESS_LEN)
46
+ return null;
47
+ const meta = data.slice(REGISTRY_ENTRY_META_OFFSET, REGISTRY_ENTRY_META_OFFSET + META_ADDRESS_LEN);
48
+ return ("0x" + bytesToHex(meta));
49
+ }
50
+ /**
51
+ * Resolve a registrant's 66-byte stealth meta-address via the registry PDA, or `null` when
52
+ * unregistered.
53
+ */
54
+ export async function resolveMetaAddress(connection, params) {
55
+ const registrant = typeof params.registrant === "string"
56
+ ? new PublicKey(params.registrant)
57
+ : params.registrant;
58
+ const pda = getRegistryEntryPda(params.registryProgramId, registrant);
59
+ const info = await connection.getAccountInfo(pda);
60
+ if (!info?.data)
61
+ return null;
62
+ return decodeRegistryEntryMetaAddress(new Uint8Array(info.data));
63
+ }
64
+ /** Whether `registrant` has a stealth meta-address registered. */
65
+ export async function isRegistered(connection, params) {
66
+ const meta = await resolveMetaAddress(connection, params);
67
+ return meta != null && meta.length === 2 + META_ADDRESS_LEN * 2;
68
+ }
69
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * `StealthAddressAnnouncer::announce_with_relay` (Solana) — cross-chain announcements over the
3
+ * Universal Announcement Bus. Emits the local `Announcement` AND publishes the 96-byte cross-chain
4
+ * payload (`spec/payload-format.md`) through the Wormhole Core Bridge via CPI.
5
+ *
6
+ * The Solana counterpart to `@opaquecash/uab`'s EVM `announceWithRelay`. Unlike the EVM path there
7
+ * is no `consistencyLevel` arg; the program takes a Wormhole `batch_id` (nonce) and the current
8
+ * `wormhole_fee` (lamports; 0 on devnet). The `wormhole_message` account is a fresh signer the
9
+ * caller must co-sign with — use {@link buildAnnounceWithRelay} to mint one alongside the
10
+ * instruction.
11
+ *
12
+ * Account order (IDL-authoritative, `solana/target/idl/stealth_announcer.json`):
13
+ * `caller(s,w)`, `wormhole_emitter` (PDA `["emitter"]` of the announcer), `wormhole_config`
14
+ * (PDA `["Bridge"]` of the core), `wormhole_fee_collector` (PDA `["fee_collector"]`),
15
+ * `wormhole_sequence` (PDA `["Sequence", emitter]`), `wormhole_message(s,w)`, `wormhole_program`,
16
+ * clock, rent, system_program.
17
+ */
18
+ import { Connection, Keypair, PublicKey, TransactionInstruction } from "@solana/web3.js";
19
+ /** Derive this announcer program's Wormhole emitter PDA (`["emitter"]`). */
20
+ export declare function deriveWormholeEmitterPda(announcerProgramId: PublicKey): PublicKey;
21
+ /** Derive the Wormhole Core Bridge config PDA (`["Bridge"]`). */
22
+ export declare function deriveWormholeConfigPda(wormholeCore: PublicKey): PublicKey;
23
+ /** Derive the Wormhole Core Bridge fee-collector PDA (`["fee_collector"]`). */
24
+ export declare function deriveWormholeFeeCollectorPda(wormholeCore: PublicKey): PublicKey;
25
+ /** Derive the per-emitter Wormhole sequence PDA (`["Sequence", emitter]`). */
26
+ export declare function deriveWormholeSequencePda(wormholeCore: PublicKey, emitter: PublicKey): PublicKey;
27
+ /**
28
+ * Read the current Wormhole message fee (lamports) from the core bridge config account. Returns
29
+ * `0n` when the account is missing or too short (devnet charges no fee). Pass the result as
30
+ * {@link AnnounceWithRelayInstructionParams.wormholeFee}.
31
+ */
32
+ export declare function fetchWormholeMessageFee(connection: Connection, wormholeCore: PublicKey): Promise<bigint>;
33
+ /** Parameters for {@link buildAnnounceWithRelayInstruction}. */
34
+ export interface AnnounceWithRelayInstructionParams {
35
+ announcerProgramId: PublicKey;
36
+ /** Wormhole Core Bridge program id (from the {@link import("./programs.js").SolanaDeployment}). */
37
+ wormholeCore: PublicKey;
38
+ /** Signs and pays; on-chain `caller` of the announcement. */
39
+ caller: PublicKey;
40
+ /** Fresh ephemeral message account (signer + writable); use {@link buildAnnounceWithRelay} to mint. */
41
+ wormholeMessage: PublicKey;
42
+ /** Stealth address bytes (1..=32; Opaque uses the 20-byte EVM-style address). */
43
+ stealthAddress: Uint8Array;
44
+ /** 33-byte compressed secp256k1 ephemeral public key. */
45
+ ephemeralPubKey: Uint8Array;
46
+ /** Metadata; `metadata[0]` MUST be the view tag (<= 24 bytes for the cross-chain budget). */
47
+ metadata: Uint8Array;
48
+ schemeId?: bigint;
49
+ /** Wormhole nonce / batch id (default 0). */
50
+ batchId?: number;
51
+ /** Wormhole message fee in lamports (default 0; devnet is 0). See {@link fetchWormholeMessageFee}. */
52
+ wormholeFee?: bigint;
53
+ }
54
+ /**
55
+ * Build an `announce_with_relay` instruction. The `caller` AND the `wormholeMessage` account must
56
+ * both sign the transaction (the message account is consumed by the core bridge `post_message`
57
+ * CPI). Prefer {@link buildAnnounceWithRelay}, which mints the message keypair for you.
58
+ */
59
+ export declare function buildAnnounceWithRelayInstruction(params: AnnounceWithRelayInstructionParams): TransactionInstruction;
60
+ /** An `announce_with_relay` instruction plus the ephemeral message keypair that must co-sign. */
61
+ export interface AnnounceWithRelayBuild {
62
+ instruction: TransactionInstruction;
63
+ /** Fresh Wormhole message account; add to the transaction's signers alongside the caller. */
64
+ messageKeypair: Keypair;
65
+ }
66
+ /**
67
+ * Mint a fresh Wormhole message keypair and build the `announce_with_relay` instruction against it.
68
+ * The returned `messageKeypair` MUST be included as an additional signer when sending the tx.
69
+ */
70
+ export declare function buildAnnounceWithRelay(params: Omit<AnnounceWithRelayInstructionParams, "wormholeMessage">): AnnounceWithRelayBuild;
71
+ //# sourceMappingURL=relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,UAAU,EACV,OAAO,EACP,SAAS,EAIT,sBAAsB,EACvB,MAAM,iBAAiB,CAAC;AAiBzB,4EAA4E;AAC5E,wBAAgB,wBAAwB,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAEjF;AAED,iEAAiE;AACjE,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,SAAS,GAAG,SAAS,CAE1E;AAED,+EAA+E;AAC/E,wBAAgB,6BAA6B,CAAC,YAAY,EAAE,SAAS,GAAG,SAAS,CAEhF;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,SAAS,EACvB,OAAO,EAAE,SAAS,GACjB,SAAS,CAKX;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,UAAU,EACtB,YAAY,EAAE,SAAS,GACtB,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,gEAAgE;AAChE,MAAM,WAAW,kCAAkC;IACjD,kBAAkB,EAAE,SAAS,CAAC;IAC9B,mGAAmG;IACnG,YAAY,EAAE,SAAS,CAAC;IACxB,6DAA6D;IAC7D,MAAM,EAAE,SAAS,CAAC;IAClB,uGAAuG;IACvG,eAAe,EAAE,SAAS,CAAC;IAC3B,iFAAiF;IACjF,cAAc,EAAE,UAAU,CAAC;IAC3B,yDAAyD;IACzD,eAAe,EAAE,UAAU,CAAC;IAC5B,6FAA6F;IAC7F,QAAQ,EAAE,UAAU,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sGAAsG;IACtG,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,kCAAkC,GACzC,sBAAsB,CAiCxB;AAED,iGAAiG;AACjG,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,sBAAsB,CAAC;IACpC,6FAA6F;IAC7F,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,IAAI,CAAC,kCAAkC,EAAE,iBAAiB,CAAC,GAClE,sBAAsB,CAOxB"}
package/dist/relay.js ADDED
@@ -0,0 +1,102 @@
1
+ /**
2
+ * `StealthAddressAnnouncer::announce_with_relay` (Solana) — cross-chain announcements over the
3
+ * Universal Announcement Bus. Emits the local `Announcement` AND publishes the 96-byte cross-chain
4
+ * payload (`spec/payload-format.md`) through the Wormhole Core Bridge via CPI.
5
+ *
6
+ * The Solana counterpart to `@opaquecash/uab`'s EVM `announceWithRelay`. Unlike the EVM path there
7
+ * is no `consistencyLevel` arg; the program takes a Wormhole `batch_id` (nonce) and the current
8
+ * `wormhole_fee` (lamports; 0 on devnet). The `wormhole_message` account is a fresh signer the
9
+ * caller must co-sign with — use {@link buildAnnounceWithRelay} to mint one alongside the
10
+ * instruction.
11
+ *
12
+ * Account order (IDL-authoritative, `solana/target/idl/stealth_announcer.json`):
13
+ * `caller(s,w)`, `wormhole_emitter` (PDA `["emitter"]` of the announcer), `wormhole_config`
14
+ * (PDA `["Bridge"]` of the core), `wormhole_fee_collector` (PDA `["fee_collector"]`),
15
+ * `wormhole_sequence` (PDA `["Sequence", emitter]`), `wormhole_message(s,w)`, `wormhole_program`,
16
+ * clock, rent, system_program.
17
+ */
18
+ import { Keypair, PublicKey, SystemProgram, SYSVAR_CLOCK_PUBKEY, SYSVAR_RENT_PUBKEY, TransactionInstruction, } from "@solana/web3.js";
19
+ import { concatBytes, u32le, u64le, vecU8 } from "./bytes.js";
20
+ import { ANNOUNCE_WITH_RELAY_DISCRIMINATOR, SCHEME_ID_SECP256K1 } from "./programs.js";
21
+ const EMITTER_SEED = new TextEncoder().encode("emitter");
22
+ const BRIDGE_SEED = new TextEncoder().encode("Bridge");
23
+ const FEE_COLLECTOR_SEED = new TextEncoder().encode("fee_collector");
24
+ const SEQUENCE_SEED = new TextEncoder().encode("Sequence");
25
+ /**
26
+ * Byte offset of the `fee` (u64 LE) field inside the Wormhole Core Bridge config ("Bridge" PDA)
27
+ * account. The core bridge is a native (non-Anchor) program, so there is no 8-byte discriminator:
28
+ * `guardian_set_index: u32 (4) || last_lamports: u64 (8) || guardian_set_expiration_time: u32 (4)`
29
+ * precede `fee: u64`, putting it at offset 16.
30
+ */
31
+ const WORMHOLE_FEE_OFFSET = 16;
32
+ /** Derive this announcer program's Wormhole emitter PDA (`["emitter"]`). */
33
+ export function deriveWormholeEmitterPda(announcerProgramId) {
34
+ return PublicKey.findProgramAddressSync([EMITTER_SEED], announcerProgramId)[0];
35
+ }
36
+ /** Derive the Wormhole Core Bridge config PDA (`["Bridge"]`). */
37
+ export function deriveWormholeConfigPda(wormholeCore) {
38
+ return PublicKey.findProgramAddressSync([BRIDGE_SEED], wormholeCore)[0];
39
+ }
40
+ /** Derive the Wormhole Core Bridge fee-collector PDA (`["fee_collector"]`). */
41
+ export function deriveWormholeFeeCollectorPda(wormholeCore) {
42
+ return PublicKey.findProgramAddressSync([FEE_COLLECTOR_SEED], wormholeCore)[0];
43
+ }
44
+ /** Derive the per-emitter Wormhole sequence PDA (`["Sequence", emitter]`). */
45
+ export function deriveWormholeSequencePda(wormholeCore, emitter) {
46
+ return PublicKey.findProgramAddressSync([SEQUENCE_SEED, emitter.toBuffer()], wormholeCore)[0];
47
+ }
48
+ /**
49
+ * Read the current Wormhole message fee (lamports) from the core bridge config account. Returns
50
+ * `0n` when the account is missing or too short (devnet charges no fee). Pass the result as
51
+ * {@link AnnounceWithRelayInstructionParams.wormholeFee}.
52
+ */
53
+ export async function fetchWormholeMessageFee(connection, wormholeCore) {
54
+ const config = deriveWormholeConfigPda(wormholeCore);
55
+ const info = await connection.getAccountInfo(config);
56
+ const data = info?.data;
57
+ if (!data || data.length < WORMHOLE_FEE_OFFSET + 8)
58
+ return 0n;
59
+ return new DataView(data.buffer, data.byteOffset + WORMHOLE_FEE_OFFSET, 8).getBigUint64(0, true);
60
+ }
61
+ /**
62
+ * Build an `announce_with_relay` instruction. The `caller` AND the `wormholeMessage` account must
63
+ * both sign the transaction (the message account is consumed by the core bridge `post_message`
64
+ * CPI). Prefer {@link buildAnnounceWithRelay}, which mints the message keypair for you.
65
+ */
66
+ export function buildAnnounceWithRelayInstruction(params) {
67
+ const schemeId = params.schemeId ?? SCHEME_ID_SECP256K1;
68
+ const emitter = deriveWormholeEmitterPda(params.announcerProgramId);
69
+ const config = deriveWormholeConfigPda(params.wormholeCore);
70
+ const feeCollector = deriveWormholeFeeCollectorPda(params.wormholeCore);
71
+ const sequence = deriveWormholeSequencePda(params.wormholeCore, emitter);
72
+ const data = concatBytes(ANNOUNCE_WITH_RELAY_DISCRIMINATOR, u64le(schemeId), vecU8(params.stealthAddress), vecU8(params.ephemeralPubKey), vecU8(params.metadata), u32le(params.batchId ?? 0), u64le(params.wormholeFee ?? 0n));
73
+ return new TransactionInstruction({
74
+ programId: params.announcerProgramId,
75
+ keys: [
76
+ { pubkey: params.caller, isSigner: true, isWritable: true },
77
+ { pubkey: emitter, isSigner: false, isWritable: false },
78
+ { pubkey: config, isSigner: false, isWritable: true },
79
+ { pubkey: feeCollector, isSigner: false, isWritable: true },
80
+ { pubkey: sequence, isSigner: false, isWritable: true },
81
+ { pubkey: params.wormholeMessage, isSigner: true, isWritable: true },
82
+ { pubkey: params.wormholeCore, isSigner: false, isWritable: false },
83
+ { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
84
+ { pubkey: SYSVAR_RENT_PUBKEY, isSigner: false, isWritable: false },
85
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
86
+ ],
87
+ data: Buffer.from(data),
88
+ });
89
+ }
90
+ /**
91
+ * Mint a fresh Wormhole message keypair and build the `announce_with_relay` instruction against it.
92
+ * The returned `messageKeypair` MUST be included as an additional signer when sending the tx.
93
+ */
94
+ export function buildAnnounceWithRelay(params) {
95
+ const messageKeypair = Keypair.generate();
96
+ const instruction = buildAnnounceWithRelayInstruction({
97
+ ...params,
98
+ wormholeMessage: messageKeypair.publicKey,
99
+ });
100
+ return { instruction, messageKeypair };
101
+ }
102
+ //# sourceMappingURL=relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}
package/dist/sns.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * SNS (Solana Name Service) reads for ONS resolution path 3 (spec/ONS.md §7): an
3
+ * existing `.sol` domain publishes its CSAP meta-address in an SNS **Records V2**
4
+ * TXT record (CSAP §2.9 — the value is the `st:opq:`-prefixed / raw-hex 66-byte
5
+ * serialisation, self-describing so it coexists with other TXT uses).
6
+ */
7
+ import { Connection } from "@solana/web3.js";
8
+ /** Strip a trailing `.sol` and lowercase: `"Bob.sol"` → `"bob"`. */
9
+ export declare function snsDomainName(input: string): string;
10
+ /**
11
+ * Read the TXT Records V2 content of a `.sol` domain (`"bob.sol"` or `"bob"`),
12
+ * or `null` when the domain or record does not exist. The caller validates the
13
+ * value as a meta-address (CSAP §2.9 point validation).
14
+ */
15
+ export declare function fetchSnsTxtRecord(connection: Connection, domain: string): Promise<string | null>;
16
+ //# sourceMappingURL=sns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sns.d.ts","sourceRoot":"","sources":["../src/sns.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgC7C,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASxB"}
package/dist/sns.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * SNS (Solana Name Service) reads for ONS resolution path 3 (spec/ONS.md §7): an
3
+ * existing `.sol` domain publishes its CSAP meta-address in an SNS **Records V2**
4
+ * TXT record (CSAP §2.9 — the value is the `st:opq:`-prefixed / raw-hex 66-byte
5
+ * serialisation, self-describing so it coexists with other TXT uses).
6
+ */
7
+ let bonfidaPromise;
8
+ function loadBonfida() {
9
+ bonfidaPromise ??= import("@bonfida/spl-name-service")
10
+ .then((m) => m)
11
+ .catch(async () => {
12
+ const { createRequire } = await import("node:module");
13
+ const require = createRequire(import.meta.url);
14
+ return require("@bonfida/spl-name-service");
15
+ });
16
+ return bonfidaPromise;
17
+ }
18
+ /** Strip a trailing `.sol` and lowercase: `"Bob.sol"` → `"bob"`. */
19
+ export function snsDomainName(input) {
20
+ return input.toLowerCase().replace(/\.sol$/, "");
21
+ }
22
+ /**
23
+ * Read the TXT Records V2 content of a `.sol` domain (`"bob.sol"` or `"bob"`),
24
+ * or `null` when the domain or record does not exist. The caller validates the
25
+ * value as a meta-address (CSAP §2.9 point validation).
26
+ */
27
+ export async function fetchSnsTxtRecord(connection, domain) {
28
+ const bonfida = await loadBonfida();
29
+ try {
30
+ const res = await bonfida.getRecordV2(connection, snsDomainName(domain), bonfida.Record.TXT);
31
+ const content = res.retrievedRecord.getContent();
32
+ return bonfida.deserializeRecordV2Content(content, bonfida.Record.TXT);
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ //# sourceMappingURL=sns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sns.js","sourceRoot":"","sources":["../src/sns.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqBH,IAAI,cAAqD,CAAC;AAE1D,SAAS,WAAW;IAClB,cAAc,KAAK,MAAM,CAAC,2BAA2B,CAAC;SACnD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAgC,CAAC;SAC7C,KAAK,CAAC,KAAK,IAAI,EAAE;QAChB,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/C,OAAO,OAAO,CAAC,2BAA2B,CAAqB,CAAC;IAClE,CAAC,CAAC,CAAC;IACL,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAsB,EACtB,MAAc;IAEd,MAAM,OAAO,GAAG,MAAM,WAAW,EAAE,CAAC;IACpC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,UAAU,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7F,MAAM,OAAO,GAAG,GAAG,CAAC,eAAe,CAAC,UAAU,EAAE,CAAC;QACjD,OAAO,OAAO,CAAC,0BAA0B,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Deterministic one-time Solana destinations for stealth funds.
3
+ *
4
+ * The DKSAP stealth point is a secp256k1 key (shared with Ethereum), but Solana accounts are
5
+ * ed25519. Sender and recipient — who can both reconstruct the same stealth secp256k1 point —
6
+ * derive the same ed25519 Solana keypair via `Keypair.fromSeed(sha256("opaque-solana-stealth-v1"
7
+ * || uncompressedStealthPubKey))`. This agrees on the destination without leaking linkage to the
8
+ * recipient's main wallet. Ported from `solana/frontend/src/lib/stealth.ts`.
9
+ */
10
+ import { Keypair } from "@solana/web3.js";
11
+ /** Derive the deterministic Solana keypair from the uncompressed (65-byte) stealth pubkey. */
12
+ export declare function deriveStealthSolanaKeypair(stealthPubKeyUncompressed: Uint8Array): Keypair;
13
+ /** Base58 Solana address for the uncompressed stealth pubkey. */
14
+ export declare function deriveStealthSolanaAddress(stealthPubKeyUncompressed: Uint8Array): string;
15
+ /**
16
+ * Derive the deterministic Solana keypair from a reconstructed 32-byte secp256k1 stealth
17
+ * private key (recipient side).
18
+ */
19
+ export declare function deriveStealthSolanaKeypairFromStealthPrivKey(stealthPrivKey: Uint8Array): Keypair;
20
+ /** Base58 Solana address from a reconstructed 32-byte secp256k1 stealth private key. */
21
+ export declare function deriveStealthSolanaAddressFromStealthPrivKey(stealthPrivKey: Uint8Array): string;
22
+ //# sourceMappingURL=stealth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stealth.d.ts","sourceRoot":"","sources":["../src/stealth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAO1C,8FAA8F;AAC9F,wBAAgB,0BAA0B,CACxC,yBAAyB,EAAE,UAAU,GACpC,OAAO,CAIT;AAED,iEAAiE;AACjE,wBAAgB,0BAA0B,CACxC,yBAAyB,EAAE,UAAU,GACpC,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,4CAA4C,CAC1D,cAAc,EAAE,UAAU,GACzB,OAAO,CAGT;AAED,wFAAwF;AACxF,wBAAgB,4CAA4C,CAC1D,cAAc,EAAE,UAAU,GACzB,MAAM,CAGR"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Deterministic one-time Solana destinations for stealth funds.
3
+ *
4
+ * The DKSAP stealth point is a secp256k1 key (shared with Ethereum), but Solana accounts are
5
+ * ed25519. Sender and recipient — who can both reconstruct the same stealth secp256k1 point —
6
+ * derive the same ed25519 Solana keypair via `Keypair.fromSeed(sha256("opaque-solana-stealth-v1"
7
+ * || uncompressedStealthPubKey))`. This agrees on the destination without leaking linkage to the
8
+ * recipient's main wallet. Ported from `solana/frontend/src/lib/stealth.ts`.
9
+ */
10
+ import { Keypair } from "@solana/web3.js";
11
+ import { secp256k1 } from "@noble/curves/secp256k1";
12
+ import { sha256 } from "@noble/hashes/sha2";
13
+ import { concatBytes } from "./bytes.js";
14
+ const STEALTH_SOLANA_DOMAIN = "opaque-solana-stealth-v1";
15
+ /** Derive the deterministic Solana keypair from the uncompressed (65-byte) stealth pubkey. */
16
+ export function deriveStealthSolanaKeypair(stealthPubKeyUncompressed) {
17
+ const domain = new TextEncoder().encode(STEALTH_SOLANA_DOMAIN);
18
+ const seed = sha256(concatBytes(domain, stealthPubKeyUncompressed));
19
+ return Keypair.fromSeed(seed.slice(0, 32));
20
+ }
21
+ /** Base58 Solana address for the uncompressed stealth pubkey. */
22
+ export function deriveStealthSolanaAddress(stealthPubKeyUncompressed) {
23
+ return deriveStealthSolanaKeypair(stealthPubKeyUncompressed).publicKey.toBase58();
24
+ }
25
+ /**
26
+ * Derive the deterministic Solana keypair from a reconstructed 32-byte secp256k1 stealth
27
+ * private key (recipient side).
28
+ */
29
+ export function deriveStealthSolanaKeypairFromStealthPrivKey(stealthPrivKey) {
30
+ const uncompressed = secp256k1.getPublicKey(stealthPrivKey, false);
31
+ return deriveStealthSolanaKeypair(uncompressed);
32
+ }
33
+ /** Base58 Solana address from a reconstructed 32-byte secp256k1 stealth private key. */
34
+ export function deriveStealthSolanaAddressFromStealthPrivKey(stealthPrivKey) {
35
+ const uncompressed = secp256k1.getPublicKey(stealthPrivKey, false);
36
+ return deriveStealthSolanaAddress(uncompressed);
37
+ }
38
+ //# sourceMappingURL=stealth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stealth.js","sourceRoot":"","sources":["../src/stealth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAEzD,8FAA8F;AAC9F,MAAM,UAAU,0BAA0B,CACxC,yBAAqC;IAErC,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC/D,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAC,CAAC;IACpE,OAAO,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,0BAA0B,CACxC,yBAAqC;IAErC,OAAO,0BAA0B,CAAC,yBAAyB,CAAC,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;AACpF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,4CAA4C,CAC1D,cAA0B;IAE1B,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACnE,OAAO,0BAA0B,CAAC,YAAY,CAAC,CAAC;AAClD,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,4CAA4C,CAC1D,cAA0B;IAE1B,MAAM,YAAY,GAAG,SAAS,CAAC,YAAY,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACnE,OAAO,0BAA0B,CAAC,YAAY,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Sweep native SOL out of a one-time stealth account. The derived stealth keypair signs and
3
+ * pays its own fee, so the on-chain `from` is the stealth address itself (preserving
4
+ * unlinkability). Ported from `solana/frontend/src/lib/stealthLifecycle.ts`.
5
+ */
6
+ import { Connection, Keypair, PublicKey, Transaction, type Finality } from "@solana/web3.js";
7
+ /** A prepared full-balance sweep (transaction not yet signed/sent). */
8
+ export interface StealthSweepPlan {
9
+ transaction: Transaction;
10
+ fromPubkey: PublicKey;
11
+ destination: PublicKey;
12
+ balanceLamports: bigint;
13
+ feeLamports: bigint;
14
+ sweepLamports: bigint;
15
+ }
16
+ /**
17
+ * Build a full-balance sweep transaction: balance minus the network fee, transferred to
18
+ * `destination`. Reads balance, blockhash, and the exact fee from the RPC.
19
+ */
20
+ export declare function buildStealthSweepTransaction(connection: Connection, params: {
21
+ stealthKeypair?: Keypair;
22
+ /** 32-byte secp256k1 stealth private key (derives the Solana keypair). */
23
+ stealthPrivKey?: Uint8Array;
24
+ destination: PublicKey | string;
25
+ commitment?: Finality;
26
+ }): Promise<StealthSweepPlan>;
27
+ /**
28
+ * Sweep the full SOL balance of a stealth account to `destination`, signed by the stealth
29
+ * keypair. Returns the confirmed signature plus the swept/fee amounts.
30
+ */
31
+ export declare function sweepStealthSol(connection: Connection, params: {
32
+ stealthKeypair?: Keypair;
33
+ stealthPrivKey?: Uint8Array;
34
+ destination: PublicKey | string;
35
+ commitment?: Finality;
36
+ }): Promise<{
37
+ signature: string;
38
+ sweepLamports: bigint;
39
+ feeLamports: bigint;
40
+ }>;
41
+ //# sourceMappingURL=sweep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep.d.ts","sourceRoot":"","sources":["../src/sweep.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,UAAU,EACV,OAAO,EACP,SAAS,EAET,WAAW,EAEX,KAAK,QAAQ,EACd,MAAM,iBAAiB,CAAC;AAMzB,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,UAAU,EAAE,SAAS,CAAC;IACtB,WAAW,EAAE,SAAS,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAiBD;;;GAGG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE;IACN,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,QAAQ,CAAC;CACvB,GACA,OAAO,CAAC,gBAAgB,CAAC,CA+C3B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE;IACN,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B,WAAW,EAAE,SAAS,GAAG,MAAM,CAAC;IAChC,UAAU,CAAC,EAAE,QAAQ,CAAC;CACvB,GACA,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAc5E"}
package/dist/sweep.js ADDED
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Sweep native SOL out of a one-time stealth account. The derived stealth keypair signs and
3
+ * pays its own fee, so the on-chain `from` is the stealth address itself (preserving
4
+ * unlinkability). Ported from `solana/frontend/src/lib/stealthLifecycle.ts`.
5
+ */
6
+ import { PublicKey, SystemProgram, Transaction, sendAndConfirmTransaction, } from "@solana/web3.js";
7
+ import { deriveStealthSolanaKeypairFromStealthPrivKey } from "./stealth.js";
8
+ /** Fallback per-signature fee (lamports) if `getFeeForMessage` returns null. */
9
+ const DEFAULT_FEE_LAMPORTS = 5000;
10
+ function resolveStealthKeypair(input) {
11
+ if (input.stealthKeypair)
12
+ return input.stealthKeypair;
13
+ if (input.stealthPrivKey) {
14
+ return deriveStealthSolanaKeypairFromStealthPrivKey(input.stealthPrivKey);
15
+ }
16
+ throw new Error("sweep requires stealthKeypair or stealthPrivKey");
17
+ }
18
+ function toPubkey(v) {
19
+ return typeof v === "string" ? new PublicKey(v.trim()) : v;
20
+ }
21
+ /**
22
+ * Build a full-balance sweep transaction: balance minus the network fee, transferred to
23
+ * `destination`. Reads balance, blockhash, and the exact fee from the RPC.
24
+ */
25
+ export async function buildStealthSweepTransaction(connection, params) {
26
+ const commitment = params.commitment ?? "confirmed";
27
+ const stealthKeypair = resolveStealthKeypair(params);
28
+ const fromPubkey = stealthKeypair.publicKey;
29
+ const destination = toPubkey(params.destination);
30
+ const balanceLamports = BigInt(await connection.getBalance(fromPubkey, commitment));
31
+ if (balanceLamports <= 0n) {
32
+ throw new Error("Stealth address has zero balance.");
33
+ }
34
+ const { blockhash } = await connection.getLatestBlockhash(commitment);
35
+ const probe = new Transaction({ feePayer: fromPubkey, recentBlockhash: blockhash }).add(SystemProgram.transfer({ fromPubkey, toPubkey: destination, lamports: 1 }));
36
+ const feeResult = await connection.getFeeForMessage(probe.compileMessage(), commitment);
37
+ const feeLamports = BigInt(feeResult.value ?? DEFAULT_FEE_LAMPORTS);
38
+ if (balanceLamports <= feeLamports) {
39
+ throw new Error(`Insufficient balance to cover network fee (balance ${balanceLamports}, fee ${feeLamports} lamports).`);
40
+ }
41
+ const sweepLamports = balanceLamports - feeLamports;
42
+ if (sweepLamports > BigInt(Number.MAX_SAFE_INTEGER)) {
43
+ throw new Error("Sweep amount too large to encode as JS-number lamports.");
44
+ }
45
+ const transaction = new Transaction({
46
+ feePayer: fromPubkey,
47
+ recentBlockhash: blockhash,
48
+ }).add(SystemProgram.transfer({
49
+ fromPubkey,
50
+ toPubkey: destination,
51
+ lamports: Number(sweepLamports),
52
+ }));
53
+ return {
54
+ transaction,
55
+ fromPubkey,
56
+ destination,
57
+ balanceLamports,
58
+ feeLamports,
59
+ sweepLamports,
60
+ };
61
+ }
62
+ /**
63
+ * Sweep the full SOL balance of a stealth account to `destination`, signed by the stealth
64
+ * keypair. Returns the confirmed signature plus the swept/fee amounts.
65
+ */
66
+ export async function sweepStealthSol(connection, params) {
67
+ const stealthKeypair = resolveStealthKeypair(params);
68
+ const plan = await buildStealthSweepTransaction(connection, { ...params, stealthKeypair });
69
+ const signature = await sendAndConfirmTransaction(connection, plan.transaction, [stealthKeypair], { commitment: params.commitment ?? "confirmed" });
70
+ return {
71
+ signature,
72
+ sweepLamports: plan.sweepLamports,
73
+ feeLamports: plan.feeLamports,
74
+ };
75
+ }
76
+ //# sourceMappingURL=sweep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep.js","sourceRoot":"","sources":["../src/sweep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Read Ethereum-originated announcements mirrored on Solana by the `uab-receiver`
3
+ * program. The program verifies a posted Wormhole VAA and re-emits the canonical
4
+ * 96-byte payload as a `CrossChainAnnouncement` Anchor event; this module decodes
5
+ * those events into the same chain-neutral {@link Announcement} shape as native
6
+ * announcer logs, so the universal scan loop needs no special casing.
7
+ */
8
+ import { Connection, PublicKey, type Finality } from "@solana/web3.js";
9
+ import type { Announcement } from "@opaquecash/adapter";
10
+ import { type UabPayload } from "@opaquecash/stealth-core";
11
+ /** A decoded `CrossChainAnnouncement` event from the `uab-receiver` program. */
12
+ export interface DecodedCrossChainAnnouncementEvent {
13
+ /** Wormhole chain id of the origin chain (Ethereum = 2). */
14
+ sourceChain: number;
15
+ /** Wormhole-formatted 32-byte emitter address on the origin chain. */
16
+ sourceEmitter: Uint8Array;
17
+ /** Wormhole sequence number of the carrying VAA. */
18
+ sequence: bigint;
19
+ /** Decoded canonical 96-byte payload. */
20
+ payload: UabPayload;
21
+ }
22
+ /**
23
+ * Decode a single `CrossChainAnnouncement` event payload (the bytes after the
24
+ * `Program data: ` base64 decode). Returns `null` when the discriminator does not
25
+ * match or the buffer is malformed.
26
+ */
27
+ export declare function decodeCrossChainAnnouncementEventData(data: Uint8Array): DecodedCrossChainAnnouncementEvent | null;
28
+ /** Map a decoded cross-chain event to the chain-neutral {@link Announcement} shape. */
29
+ export declare function crossChainEventToAnnouncement(event: DecodedCrossChainAnnouncementEvent, provenance?: {
30
+ txHash?: string;
31
+ cursor?: bigint;
32
+ }): Announcement;
33
+ /**
34
+ * Decode every `CrossChainAnnouncement` event in a transaction's log messages.
35
+ * Non-matching `Program data:` lines are skipped, as are payloads whose scheme id
36
+ * is not secp256k1 (scheme 1).
37
+ */
38
+ export declare function decodeCrossChainAnnouncementLogs(logs: string[], provenance?: {
39
+ txHash?: string;
40
+ cursor?: bigint;
41
+ }): Announcement[];
42
+ /**
43
+ * Fetch historical cross-chain announcements by walking recent signatures for the
44
+ * `uab-receiver` program and decoding each transaction's logs. Returns chain-neutral
45
+ * {@link Announcement}s (with the *origin* chain id) in signature order (newest first).
46
+ */
47
+ export declare function fetchCrossChainAnnouncementsRange(connection: Connection, params: {
48
+ uabReceiverProgramId: PublicKey;
49
+ /** Max signatures to scan (RPC `getSignaturesForAddress` limit; default 1000). */
50
+ limit?: number;
51
+ /** Start searching backwards from this signature (pagination). */
52
+ before?: string;
53
+ /** Search until this signature (pagination). */
54
+ until?: string;
55
+ commitment?: Finality;
56
+ }): Promise<Announcement[]>;
57
+ //# sourceMappingURL=uab-receiver.d.ts.map