@utxopia/sdk 0.1.0-alpha.1 → 0.1.0-alpha.2
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/package.json +1 -1
- package/packages/sdk/dist/config.d.ts +34 -3
- package/packages/sdk/dist/config.js +88 -18
- package/packages/sdk/dist/index.d.ts +5 -4
- package/packages/sdk/dist/index.js +5 -4
- package/packages/sdk/dist/instructions.d.ts +71 -1
- package/packages/sdk/dist/instructions.js +74 -3
- package/packages/sdk/dist/spend-doc.d.ts +63 -0
- package/packages/sdk/dist/spend-doc.js +100 -0
- package/packages/sdk/dist/stealth.d.ts +28 -0
- package/packages/sdk/dist/stealth.js +36 -0
- package/packages/sdk/dist/taproot.d.ts +11 -0
- package/packages/sdk/dist/taproot.js +19 -0
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ import { type Address } from "@solana/kit";
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function address(input: string): Address;
|
|
22
22
|
export type NetworkType = "devnet" | "mainnet" | "localnet";
|
|
23
|
-
export type AppNetworkId = NetworkType | "devnet-regtest";
|
|
23
|
+
export type AppNetworkId = NetworkType | "devnet-regtest" | "devnet-testnet4";
|
|
24
24
|
export interface NetworkConfig {
|
|
25
25
|
/** Network identifier */
|
|
26
26
|
network: NetworkType;
|
|
@@ -99,10 +99,41 @@ export declare const CHADBUFFER_PROGRAM_ID: Address;
|
|
|
99
99
|
/** ChadBuffer Program ID for localnet testing */
|
|
100
100
|
export declare const LOCALNET_CHADBUFFER_PROGRAM_ID: Address;
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
102
|
+
* Greenfield devnet + Bitcoin testnet4 deployment (2026-08-26).
|
|
103
|
+
*
|
|
104
|
+
* The previous deployment's programs were closed on chain, so this is not a redeploy and none
|
|
105
|
+
* of the old addresses resolve any more — anything still pinned to CvfSyACR… is talking to a
|
|
106
|
+
* program that no longer exists.
|
|
107
|
+
*
|
|
108
|
+
* Two pools were deployed, each with its own Ika DKG and therefore its own dWallet. The default
|
|
109
|
+
* below is the OPEN pool. Select the verified pool by overriding `zkbtcMint` and
|
|
110
|
+
* `ikaDwalletXOnlyPubkey` — `getConfig` re-derives poolStatePda, commitmentTreePda and poolVault
|
|
111
|
+
* from the mint, so those two values are all a caller needs:
|
|
112
|
+
*
|
|
113
|
+
* verified mint G78CTddWGDaNaSKQayAt7m3pzcMyaUNxgR8y3R34YvEv
|
|
114
|
+
* state Eqn9SmFYtacrdfPE9Shbi8bDXLjCNNz3WhHfqtJnwbKY
|
|
115
|
+
* xonly 16c563baa11bfc8fe93acafe8fe169b954b3ead3ecda7754c615dec9e840b5a5
|
|
116
|
+
*
|
|
117
|
+
* Read off chain, not from the deploy notes: Eqn9SmFY… is the pool whose PoolState flags carry
|
|
118
|
+
* the permissioned bit (0b10 at offset 2). The deployment also contains a THIRD pool —
|
|
119
|
+
* 3chHiDqM… / 7wDtDd1u… — which the deploy notes name as the verified one but which is not
|
|
120
|
+
* permissioned and whose mint has zero supply. Treat it as abandoned; do not wire it up.
|
|
104
121
|
*/
|
|
105
122
|
export declare const DEVNET_CONFIG: NetworkConfig;
|
|
123
|
+
/**
|
|
124
|
+
* Solana devnet + Bitcoin **regtest** deployment — the environment app.utxopia.com serves.
|
|
125
|
+
*
|
|
126
|
+
* This is a different deployment from DEVNET_CONFIG above, not a Bitcoin-side variation of it:
|
|
127
|
+
* a different program (CvfSyACR…), a different pool, a different Ika dWallet. It used to share
|
|
128
|
+
* DEVNET_CONFIG and differ only by bitcoinNetwork, which worked while one program hosted both.
|
|
129
|
+
* It no longer does — after 2026-08-26 there are two programs — so `devnet-regtest` resolving
|
|
130
|
+
* to DEVNET_CONFIG would silently point this environment at testnet4's program and pool.
|
|
131
|
+
*
|
|
132
|
+
* Pool addresses derived from the mint with the same seeds getConfig uses, then confirmed on
|
|
133
|
+
* devnet: pool_state and commitment_tree owned by CvfSyACR… at 332 and 8816 bytes, the vault by
|
|
134
|
+
* Token-2022 at 170.
|
|
135
|
+
*/
|
|
136
|
+
export declare const DEVNET_REGTEST_CONFIG: NetworkConfig;
|
|
106
137
|
/**
|
|
107
138
|
* Mainnet Configuration (placeholder - not yet deployed)
|
|
108
139
|
*/
|
|
@@ -44,21 +44,42 @@ export const LOCALNET_CHADBUFFER_PROGRAM_ID = address("EgWyMVFZewHmjJ9GGvVBTyaC3
|
|
|
44
44
|
// Network Configurations
|
|
45
45
|
// =============================================================================
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
47
|
+
* Greenfield devnet + Bitcoin testnet4 deployment (2026-08-26).
|
|
48
|
+
*
|
|
49
|
+
* The previous deployment's programs were closed on chain, so this is not a redeploy and none
|
|
50
|
+
* of the old addresses resolve any more — anything still pinned to CvfSyACR… is talking to a
|
|
51
|
+
* program that no longer exists.
|
|
52
|
+
*
|
|
53
|
+
* Two pools were deployed, each with its own Ika DKG and therefore its own dWallet. The default
|
|
54
|
+
* below is the OPEN pool. Select the verified pool by overriding `zkbtcMint` and
|
|
55
|
+
* `ikaDwalletXOnlyPubkey` — `getConfig` re-derives poolStatePda, commitmentTreePda and poolVault
|
|
56
|
+
* from the mint, so those two values are all a caller needs:
|
|
57
|
+
*
|
|
58
|
+
* verified mint G78CTddWGDaNaSKQayAt7m3pzcMyaUNxgR8y3R34YvEv
|
|
59
|
+
* state Eqn9SmFYtacrdfPE9Shbi8bDXLjCNNz3WhHfqtJnwbKY
|
|
60
|
+
* xonly 16c563baa11bfc8fe93acafe8fe169b954b3ead3ecda7754c615dec9e840b5a5
|
|
61
|
+
*
|
|
62
|
+
* Read off chain, not from the deploy notes: Eqn9SmFY… is the pool whose PoolState flags carry
|
|
63
|
+
* the permissioned bit (0b10 at offset 2). The deployment also contains a THIRD pool —
|
|
64
|
+
* 3chHiDqM… / 7wDtDd1u… — which the deploy notes name as the verified one but which is not
|
|
65
|
+
* permissioned and whose mint has zero supply. Treat it as abandoned; do not wire it up.
|
|
49
66
|
*/
|
|
50
67
|
export const DEVNET_CONFIG = {
|
|
51
68
|
network: "devnet",
|
|
52
|
-
utxopiaProgramId: address("
|
|
69
|
+
utxopiaProgramId: address("28z2AtKA6aFGrGCh4ns1rmp7vGpWuh6x3H7gXKBcfxur"),
|
|
53
70
|
policyProgramId: address("9asWYKVriWGpExW5xM44ChHjZtispkLCiWKkM8SQi8Rs"),
|
|
54
|
-
|
|
71
|
+
// testnet4 light client, deployed 2026-08-26. Must match the `devnet` arm of
|
|
72
|
+
// BTC_LIGHT_CLIENT_PROGRAM_ID in programs/utxopia/src/constants.rs.
|
|
73
|
+
btcLightClientProgramId: address("4LZbktiNsiVAe2bwPCTPNgqiWWgZNUj4T3bDx8GZmehv"),
|
|
55
74
|
chadbufferProgramId: CHADBUFFER_PROGRAM_ID,
|
|
56
75
|
token2022ProgramId: TOKEN_2022_PROGRAM_ID,
|
|
57
76
|
ataProgramId: ATA_PROGRAM_ID,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
// Open pool. Derived and then confirmed on chain: pool_state and commitment_tree are owned by
|
|
78
|
+
// the program (332 / 8816 bytes), the vault by Token-2022 (170 bytes).
|
|
79
|
+
poolStatePda: address("FezM7ksBwftqrd4TtabMa9uXt51eCT5M946YnpSEQZHm"),
|
|
80
|
+
commitmentTreePda: address("CHwJZqNAUag6ARDfmntvmbS4WeAsGX67f2vALxXbB6PP"),
|
|
81
|
+
zkbtcMint: address("87zWstDnNgMig2vk8q8jTrK6YTcyugeRTanfT3LfyU3T"),
|
|
82
|
+
poolVault: address("8VMfH4mDizU6nHybQumxeauyiEL2QTqrGQLXmAr9wpC3"),
|
|
62
83
|
// RPC Endpoints
|
|
63
84
|
solanaRpcUrl: "https://api.devnet.solana.com",
|
|
64
85
|
solanaWsUrl: "wss://api.devnet.solana.com",
|
|
@@ -67,8 +88,11 @@ export const DEVNET_CONFIG = {
|
|
|
67
88
|
esploraUrl: "https://mempool.space/testnet4/api",
|
|
68
89
|
// Circuit CDN (Groth16 artifacts: .wasm, .zkey files)
|
|
69
90
|
circuitCdnUrl: "https://circuit.utxopia.com/circuits/v2/groth16",
|
|
70
|
-
// Groth16
|
|
71
|
-
|
|
91
|
+
// Groth16 verification is inline in the UTXOpia program, so this is the same address.
|
|
92
|
+
// It used to hold a stale third id, which `getConfig` silently overwrote with the program id
|
|
93
|
+
// whenever a programId override was supplied — meaning the constant was only ever read on the
|
|
94
|
+
// no-override path, where it was wrong.
|
|
95
|
+
groth16VerifierProgramId: address("28z2AtKA6aFGrGCh4ns1rmp7vGpWuh6x3H7gXKBcfxur"),
|
|
72
96
|
// VK Hashes (SHA256 of serialized VK bytes, generated from circom trusted setup)
|
|
73
97
|
vkHashes: {
|
|
74
98
|
claim: "7af0e702e7b595fbdb62fd268e6c529481003e07957e0f60e4fb23cd9fe6a77f",
|
|
@@ -88,8 +112,12 @@ export const DEVNET_CONFIG = {
|
|
|
88
112
|
"1x4": "01728b82e810a8ba604cc66aa6a563444d18f4598c402d11767d0a7e5049a9be",
|
|
89
113
|
"4x1": "0362b306b17dae916d836d9448a26c97e51b1b0a1a0ed052ebfbd4800e5000cf",
|
|
90
114
|
},
|
|
91
|
-
// Ika dWallet x-only pubkey
|
|
92
|
-
|
|
115
|
+
// Ika dWallet x-only pubkey for the OPEN pool, read from its PoolConfig PDA
|
|
116
|
+
// (GQ5ZfD4tJmgcquHLAHSbmAm72Foi9hf3VduPrroysM1b, offset 68..100) on 2026-08-26.
|
|
117
|
+
// All-zero here used to mean "deposit addresses cannot be derived until sync-env.sh runs";
|
|
118
|
+
// the two pools have distinct dWallets, so a single synced value could only ever be right
|
|
119
|
+
// for one of them. See the verified pool's key in the header comment above.
|
|
120
|
+
ikaDwalletXOnlyPubkey: "3a6ab80ba14bc050f048ee3e0b77d8935adf4fc5e2f5947311f26cc2cb5bd194",
|
|
93
121
|
// SNS Subdomain Resolution (devnet)
|
|
94
122
|
snsNameServiceProgramId: "namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX", // SPL Name Service (devnet)
|
|
95
123
|
snsRegistrarProgramId: "snshBoEQ9jx4QoHBpZDQPYdNCtw7RMxJvYrKFEhwaPJ", // SNS Registrar (devnet)
|
|
@@ -99,6 +127,32 @@ export const DEVNET_CONFIG = {
|
|
|
99
127
|
snsReverseLookupClass: "7NbD1vprif6apthEZAqhRfYuhrqnuderB8qpnfXGCc8H", // Reverse lookup class (devnet)
|
|
100
128
|
snsStealthDataVersion: 1,
|
|
101
129
|
};
|
|
130
|
+
/**
|
|
131
|
+
* Solana devnet + Bitcoin **regtest** deployment — the environment app.utxopia.com serves.
|
|
132
|
+
*
|
|
133
|
+
* This is a different deployment from DEVNET_CONFIG above, not a Bitcoin-side variation of it:
|
|
134
|
+
* a different program (CvfSyACR…), a different pool, a different Ika dWallet. It used to share
|
|
135
|
+
* DEVNET_CONFIG and differ only by bitcoinNetwork, which worked while one program hosted both.
|
|
136
|
+
* It no longer does — after 2026-08-26 there are two programs — so `devnet-regtest` resolving
|
|
137
|
+
* to DEVNET_CONFIG would silently point this environment at testnet4's program and pool.
|
|
138
|
+
*
|
|
139
|
+
* Pool addresses derived from the mint with the same seeds getConfig uses, then confirmed on
|
|
140
|
+
* devnet: pool_state and commitment_tree owned by CvfSyACR… at 332 and 8816 bytes, the vault by
|
|
141
|
+
* Token-2022 at 170.
|
|
142
|
+
*/
|
|
143
|
+
export const DEVNET_REGTEST_CONFIG = {
|
|
144
|
+
...DEVNET_CONFIG,
|
|
145
|
+
utxopiaProgramId: address("CvfSyACR8xemPdeJsB3D8Xh15rKUQ3b5c1PvnmABCBJp"),
|
|
146
|
+
groth16VerifierProgramId: address("CvfSyACR8xemPdeJsB3D8Xh15rKUQ3b5c1PvnmABCBJp"),
|
|
147
|
+
btcLightClientProgramId: address("8hCSNKf8ByqZdet2D4SDiZHDrB1u9ohkhqKKzr9i7vfQ"),
|
|
148
|
+
poolStatePda: address("CeEEmE9MvFPZtqcgv1rsXmzNmfvchbs8VEZJGFKZ2Cyj"),
|
|
149
|
+
commitmentTreePda: address("45bCw97GssorJM9b1ZWZLMGy1NJUczcaNVaKASmCRohL"),
|
|
150
|
+
zkbtcMint: address("BJ5SXA33qK8r8BxJD4nQPf72ae9bactiA2Zqo33EcvPu"),
|
|
151
|
+
poolVault: address("JsZ1ipHZWiZYE8kRDXmEkKuiK6KKVxCVJKv1tGnCkM6"),
|
|
152
|
+
ikaDwalletXOnlyPubkey: "243a6c47504f82d168754da9392a9dbcbab9b9f9c515a609227fac4642b2a26f",
|
|
153
|
+
bitcoinNetwork: "regtest",
|
|
154
|
+
esploraUrl: "http://localhost:2140",
|
|
155
|
+
};
|
|
102
156
|
/**
|
|
103
157
|
* Mainnet Configuration (placeholder - not yet deployed)
|
|
104
158
|
*/
|
|
@@ -221,6 +275,24 @@ function esploraUrlForNetwork(net) {
|
|
|
221
275
|
default: return `https://mempool.space/${net}/api`;
|
|
222
276
|
}
|
|
223
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Base config for an app network id. Two devnet deployments now exist, so this cannot be
|
|
280
|
+
* derived from NetworkType alone — that is what normalizeAppNetwork collapses away.
|
|
281
|
+
*/
|
|
282
|
+
function baseConfigForAppNetwork(network) {
|
|
283
|
+
switch (network) {
|
|
284
|
+
case "localnet":
|
|
285
|
+
return LOCALNET_CONFIG;
|
|
286
|
+
case "mainnet":
|
|
287
|
+
return MAINNET_CONFIG;
|
|
288
|
+
case "devnet-regtest":
|
|
289
|
+
return DEVNET_REGTEST_CONFIG;
|
|
290
|
+
case "devnet-testnet4":
|
|
291
|
+
case "devnet":
|
|
292
|
+
default:
|
|
293
|
+
return DEVNET_CONFIG;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
224
296
|
function normalizeAppNetwork(network) {
|
|
225
297
|
switch (network) {
|
|
226
298
|
case "mainnet":
|
|
@@ -229,6 +301,7 @@ function normalizeAppNetwork(network) {
|
|
|
229
301
|
return "localnet";
|
|
230
302
|
case "devnet":
|
|
231
303
|
case "devnet-regtest":
|
|
304
|
+
case "devnet-testnet4":
|
|
232
305
|
default:
|
|
233
306
|
return "devnet";
|
|
234
307
|
}
|
|
@@ -239,6 +312,7 @@ function bitcoinNetworkForAppNetwork(network) {
|
|
|
239
312
|
case "devnet-regtest":
|
|
240
313
|
return "regtest";
|
|
241
314
|
case "devnet":
|
|
315
|
+
case "devnet-testnet4":
|
|
242
316
|
return "testnet4";
|
|
243
317
|
case "mainnet":
|
|
244
318
|
return "mainnet";
|
|
@@ -273,7 +347,7 @@ export function setConfig(network) {
|
|
|
273
347
|
const baseNetwork = normalizeAppNetwork(network);
|
|
274
348
|
switch (baseNetwork) {
|
|
275
349
|
case "devnet":
|
|
276
|
-
currentConfig =
|
|
350
|
+
currentConfig = baseConfigForAppNetwork(network);
|
|
277
351
|
break;
|
|
278
352
|
case "mainnet":
|
|
279
353
|
throw new Error("Mainnet is not yet deployed. " +
|
|
@@ -318,11 +392,7 @@ export async function initConfig(overrides) {
|
|
|
318
392
|
(typeof process !== "undefined" && (process.env?.NEXT_PUBLIC_NETWORK || process.env?.UTXOPIA_NETWORK)) ||
|
|
319
393
|
"devnet";
|
|
320
394
|
const networkId = normalizeAppNetwork(appNetworkId);
|
|
321
|
-
const baseConfig =
|
|
322
|
-
? LOCALNET_CONFIG
|
|
323
|
-
: networkId === "mainnet"
|
|
324
|
-
? MAINNET_CONFIG
|
|
325
|
-
: DEVNET_CONFIG;
|
|
395
|
+
const baseConfig = baseConfigForAppNetwork(appNetworkId);
|
|
326
396
|
const config = { ...baseConfig };
|
|
327
397
|
const appBitcoinNetwork = bitcoinNetworkForAppNetwork(appNetworkId);
|
|
328
398
|
const btcNetOverride = typeof process !== "undefined" && process.env?.NEXT_PUBLIC_BTC_NETWORK;
|
|
@@ -34,16 +34,17 @@ export { fetchTokenConfig, getTokenId, fetchSupportedTokens, fetchEnabledTokens,
|
|
|
34
34
|
export { MAGICBLOCK_DELEGATION_PROGRAM_ID, MAGICBLOCK_DEVNET_ROUTER_URL, MAGICBLOCK_DEVNET_ROUTER_WS_URL, MAGICBLOCK_EPHEMERAL_VAULT_ID, MAGICBLOCK_MAGIC_CONTEXT_ID, MAGICBLOCK_MAGIC_PROGRAM_ID, MAGICBLOCK_MAX_PER_MEMBERS, MAGICBLOCK_PERMISSION_PROGRAM_ID, MAGICBLOCK_PER_MEMBER_FLAGS, MAGICBLOCK_VALIDATOR_IDENTITIES, buildDefaultPrivacyDomain, buildMagicBlockPerMemberFlags, deriveMagicBlockCommitRecordPDA, deriveMagicBlockCommitStatePDA, deriveMagicBlockDelegateBufferPDA, deriveMagicBlockDelegationMetadataPDA, deriveMagicBlockDelegationRecordPDA, deriveMagicBlockPermissionPDA, deriveMagicBlockUndelegateBufferPDA, requiresMagicBlockEndpoint, getMagicBlockEndpoint, getMagicBlockValidatorIdentity, createMagicBlockRouterConnection, type BuildPrivacyDomainOptions, type MagicBlockEndpointConfig, type MagicBlockExecutionMode, type MagicBlockPolicyMode, type MagicBlockPerMemberFlagName, type MagicBlockValidatorRegion, type PrivacyDomainConfig, type PrivacyDomainKind, } from "./magicblock";
|
|
35
35
|
export { generateNote, createNoteFromSecrets, serializeNote, deserializeNote, noteHasComputedHashes, getNotePublicKeyX, computeNoteCommitment, computeNoteNullifier, formatBtc, parseBtc, deriveNote, deriveNotes, deriveMasterKey, deriveNoteFromMaster, estimateSeedStrength, createNote, prepareWithdrawal, createStealthNote, serializeStealthNote, deserializeStealthNote, type Note, type SerializedNote, type NoteData, type StealthNote, type SerializedStealthNote, createJoinSplitNote, computeJoinSplitNoteNullifier, serializeJoinSplitNote, deserializeJoinSplitNote, type JoinSplitNote, type SerializedJoinSplitNote, } from "./note";
|
|
36
36
|
export { createMerkleProof, createMerkleProofFromBigints, proofToCircomFormat, proofToOnChainFormat, createEmptyMerkleProof, leafIndexToPathIndices, pathIndicesToLeafIndex, validateMerkleProofStructure, parseMerkleProofResponse, TREE_DEPTH, ROOT_HISTORY_SIZE, MAX_LEAVES, ZERO_VALUE, type MerkleProof, } from "./merkle";
|
|
37
|
-
export { deriveTaprootAddress, deriveTaprootAddressWithRefund, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, type DepositDestinationChain, type DepositBitcoinNetwork, type DepositOpReturnContext, type ParsedDepositOpReturn, } from "./taproot";
|
|
37
|
+
export { deriveTaprootAddress, deriveTaprootAddressWithRefund, depositTweakCommitment, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, type DepositDestinationChain, type DepositBitcoinNetwork, type DepositOpReturnContext, type ParsedDepositOpReturn, } from "./taproot";
|
|
38
38
|
export { encodeClaimLink, decodeClaimLink, parseClaimUrl, } from "./claim-link";
|
|
39
39
|
export type { ProofData, MerkleProofInput, CircuitType, JoinSplitProofInputs, } from "./prover/web";
|
|
40
40
|
export { uploadTransactionToBuffer, uploadProofToBuffer, closeBuffer, readBufferData, fetchRawTransaction, fetchMerkleProof, prepareVerifyDeposit, buildMerkleProof, needsBuffer as bufferNeedsBuffer, getProofSource, calculateUploadTransactions, CHADBUFFER_PROGRAM_ID, AUTHORITY_SIZE, MAX_DATA_PER_WRITE, SOLANA_TX_SIZE_LIMIT, type ProofUploadResult, } from "./chadbuffer";
|
|
41
41
|
export { computeBoundParamsHash, computeSolanaDomainBoundParamsHash, computeSolanaDomainSeparator, computeStealthDataHash, createTransferBoundParams, createUnshieldBoundParams, createRedeemBoundParams, SOLANA_BOUND_CHAIN_ID, SOLANA_DEVNET_BOUND_CHAIN_ID, SOLANA_MAINNET_BOUND_CHAIN_ID, DEFAULT_BOUND_PARAMS, type BoundParams, type BoundParamsMode, type SolanaPrivacyDomainContext, type SolanaPrivacyDomainKind, } from "./bound-params";
|
|
42
|
-
export {
|
|
42
|
+
export { formatSpendDoc, renderSpendDoc, SpendDocMismatch, type SpendDoc, type SpendSignals, } from "./spend-doc";
|
|
43
|
+
export { getConfig, setConfig, createConfig, initConfig, DEVNET_CONFIG, DEVNET_REGTEST_CONFIG, MAINNET_CONFIG, LOCALNET_CONFIG, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, ATA_PROGRAM_ID, UTXOPIA_POLICY_PROGRAM_ID, SDK_VERSION, DEPLOYMENT_INFO, JOINSPLIT_TREE_DEPTH, type NetworkConfig, type NetworkType, } from "./config";
|
|
43
44
|
export { POOL_STATE_DISCRIMINATOR, POOL_STATE_LEN, POOL_STATE_OFFSETS, POOL_FLAG, parsePoolState, parsePoolFees, BPS_DENOMINATOR, computeBpsFee, feeShareBps, } from "./pool-state";
|
|
44
45
|
export type { PoolState, PoolFees } from "./pool-state";
|
|
45
46
|
export { PDA_SEEDS, poolStateSeeds, commitmentTreeSeeds, tokenConfigSeeds, poolConfigSeeds, nullifierRecordSeeds, redemptionRequestSeeds, vkRegistrySeeds, depositReceiptSeeds, policyApprovalSeeds, exitDestinationSeeds, lightClientSeeds, blockHeaderSeeds, heightIndexSeeds, verifiedTransactionSeeds, derivePoolStatePDA, deriveCommitmentTreePDA, deriveNullifierRecordPDA, derivePolicyApprovalPDA, deriveExitDestinationPDA, EXIT_KIND_SOLANA_OWNER, EXIT_KIND_BTC_SCRIPT, deriveLightClientPDA, deriveBlockHeaderPDA, deriveHeightIndexPDA, deriveVkRegistryPDA, deriveRedemptionRequestPDA, deriveTokenConfigPDA, derivePoolConfigPDA, deriveDepositReceiptPDA, commitmentToBytes, } from "./pda";
|
|
46
|
-
export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, type StealthDeposit, type StealthOutputData, type StealthOutputWithKeys, type CircuitStealthOutput, type ScannedNote, type ClaimInputs as StealthClaimInputs, type OnChainStealthAnnouncement, type ConnectionAdapter, type ViewOnlyKeys, type ViewOnlyScannedNote, createNonInteractiveDeposit, pickIkaCustodyKey, type NonInteractiveDepositResult, type NonInteractiveDepositWithRefundResult, } from "./stealth";
|
|
47
|
+
export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, createTweakDeposit, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, type StealthDeposit, type StealthOutputData, type StealthOutputWithKeys, type CircuitStealthOutput, type ScannedNote, type ClaimInputs as StealthClaimInputs, type OnChainStealthAnnouncement, type ConnectionAdapter, type ViewOnlyKeys, type ViewOnlyScannedNote, createNonInteractiveDeposit, pickIkaCustodyKey, type NonInteractiveDepositResult, type TweakDepositResult, type NonInteractiveDepositWithRefundResult, } from "./stealth";
|
|
47
48
|
export { buildDepositPsbt, estimateDepositFee, fetchUtxos, selectUtxos, type BuildDepositPsbtParams, type BuildDepositPsbtResult, type UtxoDescriptor, } from "./psbt";
|
|
48
49
|
export { EsploraClient, esploraTestnet, esploraMainnet, type EsploraTransaction, type EsploraVin, type EsploraVout, type EsploraStatus, type EsploraAddressInfo, type EsploraUtxo, type EsploraMerkleProof, type EsploraNetwork, } from "./core/esplora";
|
|
49
50
|
export { MempoolClient, mempoolTestnet, mempoolMainnet, reverseBytes, type BlockHeader, type TransactionInfo, type SPVProofData, } from "./core/mempool";
|
|
@@ -52,7 +53,7 @@ export { setDebug } from "./logger";
|
|
|
52
53
|
export { createFetchConnectionAdapter, createConnectionAdapterFromWeb3, createConnectionAdapterFromKit, getConnectionAdapter, clearConnectionAdapterCache, type RpcConfig, type Web3Connection, type KitRpc, } from "./solana/connection";
|
|
53
54
|
export { resolveSnsName, resolveStealthName, parseSnsStealthData, isSnsStealthAddress, isAuditorDisclosable, SnsComplianceFlags, SNS_COMPLIANCE_AUDITOR_OFFSET, SNS_COMPLIANCE_AUDITOR_BYTES, deriveParentDomainKey, SNS_STEALTH_DATA_SIZE, type SnsStealthAddress, } from "./sns-resolver";
|
|
54
55
|
export { COMMITMENT_TREE_DISCRIMINATOR, parseCommitmentTreeData, isValidRoot, fetchCommitmentTree, getCommitmentIndex, saveCommitmentIndex, CommitmentTreeIndex, buildCommitmentTreeFromChain, fetchLeafIndexForCommitment, fetchMerkleProofForCommitment, getMerkleProofFromTree, type CommitmentTreeState, type RpcClient, type OnChainMerkleProof, } from "./commitment-tree";
|
|
55
|
-
export { INSTRUCTION_DISCRIMINATORS, buildShieldInstructionData, buildShieldInstruction, type ShieldInstructionOptions, buildApproveRedemptionSigningInstructionData, buildApproveRedemptionSigningInstruction, buildCancelRedemptionInstructionData, buildCancelRedemptionInstruction, type CancelRedemptionInstructionOptions, bigintTo32Bytes, bytes32ToBigint, buildTransactInstructionData, buildTransactInstruction, buildRedeemInstructionData, buildUnshieldInstructionData, buildUnshieldInstruction, buildProposePoolUpdateInstructionData, buildProposePoolUpdateInstruction, buildExecutePoolUpdateInstructionData, buildExecutePoolUpdateInstruction, buildCancelPoolUpdateInstructionData, buildCancelPoolUpdateInstruction, buildRotateTreeInstructionData, buildRotateTreeInstruction, type RotateTreeOptions, buildMagicBlockDelegateInstructionData, buildMagicBlockDelegateInstruction, buildMagicBlockCommitInstructionData, buildMagicBlockCommitInstruction, buildMagicBlockPerPermissionInstructionData, buildMagicBlockPerPermissionInstruction, buildPolicyRequestHash, buildPolicyIntentParts, buildRegisterExitDestinationInstruction, buildRegisterExitDestinationInstructionData, MAX_POLICY_INTENT_PARTS, buildInitializePolicyApprovalInstructionData, buildInitializePolicyApprovalInstruction, buildPolicyApprovalDecisionInstruction, buildPolicyApprovalCommitInstruction, buildCompleteDepositPermissionedInstructionData, buildCompleteDepositPermissionedInstruction, buildShieldPermissionedInstructionData, buildShieldPermissionedInstruction, buildRotateAuditorInstructionData, buildRotateAuditorInstruction, type MagicBlockDelegateTarget, type MagicBlockDelegateInstructionOptions, type MagicBlockCommitInstructionOptions, type MagicBlockPerPermissionOperation, type MagicBlockPerPermissionMember, type MagicBlockPerPermissionInstructionOptions, type PolicyApprovalDecision, type InitializePolicyApprovalOptions, type CompleteDepositPermissionedOptions, type ShieldPermissionedInstructionOptions, type RotateAuditorOptions, buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData, buildSetPoolConfigInstructionData, parsePoolConfig, type ParsedPoolConfig, POOL_CONFIG_DISCRIMINATOR, POOL_CONFIG_LEN, POOL_SCRIPT_MAX_LEN, type Instruction, type ApproveRedemptionSigningInstructionOptions, type TransactInstructionOptions, type UnshieldInstructionOptions, type ProposePoolUpdateOptions, type ExecutePoolUpdateOptions, type CancelPoolUpdateOptions, } from "./instructions";
|
|
56
|
+
export { INSTRUCTION_DISCRIMINATORS, buildShieldInstructionData, buildShieldInstruction, type ShieldInstructionOptions, buildApproveRedemptionSigningInstructionData, buildApproveRedemptionSigningInstruction, buildCancelRedemptionInstructionData, buildCancelRedemptionInstruction, type CancelRedemptionInstructionOptions, bigintTo32Bytes, bytes32ToBigint, buildTransactInstructionData, buildTransactInstruction, buildRedeemInstructionData, buildUnshieldInstructionData, buildUnshieldInstruction, buildProposePoolUpdateInstructionData, buildProposePoolUpdateInstruction, buildExecutePoolUpdateInstructionData, buildExecutePoolUpdateInstruction, buildCancelPoolUpdateInstructionData, buildCancelPoolUpdateInstruction, buildRotateTreeInstructionData, buildRotateTreeInstruction, type RotateTreeOptions, buildMagicBlockDelegateInstructionData, buildMagicBlockDelegateInstruction, buildMagicBlockCommitInstructionData, buildMagicBlockCommitInstruction, buildMagicBlockPerPermissionInstructionData, buildMagicBlockPerPermissionInstruction, buildPolicyRequestHash, buildPolicyIntentParts, buildRegisterExitDestinationInstruction, buildRegisterExitDestinationInstructionData, MAX_POLICY_INTENT_PARTS, buildInitializePolicyApprovalInstructionData, buildInitializePolicyApprovalInstruction, buildPolicyApprovalDecisionInstruction, buildPolicyApprovalCommitInstruction, buildCompleteDepositPermissionedInstructionData, buildCompleteDepositPermissionedInstruction, buildShieldPermissionedInstructionData, buildShieldPermissionedInstruction, buildRotateAuditorInstructionData, buildRotateAuditorInstruction, type MagicBlockDelegateTarget, type MagicBlockDelegateInstructionOptions, type MagicBlockCommitInstructionOptions, type MagicBlockPerPermissionOperation, type MagicBlockPerPermissionMember, type MagicBlockPerPermissionInstructionOptions, type PolicyApprovalDecision, type InitializePolicyApprovalOptions, type CompleteDepositPermissionedOptions, type ShieldPermissionedInstructionOptions, type RotateAuditorOptions, buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData, buildVerifyDepositInstructionData, buildSetPoolConfigInstructionData, parsePoolConfig, type ParsedPoolConfig, POOL_CONFIG_DISCRIMINATOR, POOL_CONFIG_LEN, POOL_SCRIPT_MAX_LEN, type Instruction, type ApproveRedemptionSigningInstructionOptions, type TransactInstructionOptions, type UnshieldInstructionOptions, type ProposePoolUpdateOptions, type ExecutePoolUpdateOptions, type CancelPoolUpdateOptions, } from "./instructions";
|
|
56
57
|
export { VK_REGISTRY_DISCRIMINATOR, VK_REGISTRY_LEN, MAX_IC_POINTS, MAX_SAFE_JOINSPLIT_SIZE, INIT_VK_REGISTRY_DISCRIMINATOR, UPDATE_VK_REGISTRY_DISCRIMINATOR, joinSplitNumPublicInputs, computeVkHash, vkeyJsonToVkMaterial, buildVkRegistryData, parseVkRegistry, assertVkRegistryForShape, isVkRegistryReady, type JoinSplitVkMaterial, type SnarkjsVkeyJson, type ParsedVkRegistry, } from "./vk-registry";
|
|
57
58
|
export { fetchExplorerDeposits, fetchExplorerTransfers, fetchExplorerRedemptions, parseNullifierRecord, parseRedemptionRequest, NULLIFIER_RECORD_SIZE, REDEMPTION_REQUEST_SIZE, NULLIFIER_RECORD_DISCRIMINATOR, REDEMPTION_REQUEST_DISCRIMINATOR, OPERATION_TYPE_LABELS, type ExplorerDeposit, type ExplorerTransferEvent, type ExplorerRedemption, type IndexerLeaf, } from "./explorer";
|
|
58
59
|
export { parseProgramEvents, parseNullifierSpentEvent, parseStealthAnnouncementEvent, parseSenderMemoEvent, parseBtcOriginAttestationEvent, parseAuditorCiphertextEvent, EVENT_NULLIFIER_SPENT, EVENT_STEALTH_ANNOUNCEMENT, EVENT_NULLIFIERS_BATCH, EVENT_ANNOUNCEMENTS_BATCH, EVENT_SENDER_MEMO, EVENT_BTC_ORIGIN_ATTESTATION, EVENT_AUDITOR_CIPHERTEXT, type NullifierSpentEvent, type StealthAnnouncementEvent, type SenderMemoEvent, type BtcOriginAttestationEvent, type AuditorCiphertextEvent, type ProgramEvent, } from "./events";
|
|
@@ -94,7 +94,7 @@ export { createMerkleProof, createMerkleProofFromBigints, proofToCircomFormat, p
|
|
|
94
94
|
// ==========================================================================
|
|
95
95
|
// Taproot address utilities
|
|
96
96
|
// ==========================================================================
|
|
97
|
-
export { deriveTaprootAddress, deriveTaprootAddressWithRefund, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, } from "./taproot";
|
|
97
|
+
export { deriveTaprootAddress, deriveTaprootAddressWithRefund, depositTweakCommitment, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, } from "./taproot";
|
|
98
98
|
// ==========================================================================
|
|
99
99
|
// Claim link utilities
|
|
100
100
|
// ==========================================================================
|
|
@@ -107,10 +107,11 @@ export { uploadTransactionToBuffer, uploadProofToBuffer, closeBuffer, readBuffer
|
|
|
107
107
|
// Bound Parameters (JoinSplit transaction binding)
|
|
108
108
|
// ==========================================================================
|
|
109
109
|
export { computeBoundParamsHash, computeSolanaDomainBoundParamsHash, computeSolanaDomainSeparator, computeStealthDataHash, createTransferBoundParams, createUnshieldBoundParams, createRedeemBoundParams, SOLANA_BOUND_CHAIN_ID, SOLANA_DEVNET_BOUND_CHAIN_ID, SOLANA_MAINNET_BOUND_CHAIN_ID, DEFAULT_BOUND_PARAMS, } from "./bound-params";
|
|
110
|
+
export { formatSpendDoc, renderSpendDoc, SpendDocMismatch, } from "./spend-doc";
|
|
110
111
|
// ==========================================================================
|
|
111
112
|
// Configuration
|
|
112
113
|
// ==========================================================================
|
|
113
|
-
export { getConfig, setConfig, createConfig, initConfig, DEVNET_CONFIG, MAINNET_CONFIG, LOCALNET_CONFIG, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, ATA_PROGRAM_ID, UTXOPIA_POLICY_PROGRAM_ID, SDK_VERSION, DEPLOYMENT_INFO, JOINSPLIT_TREE_DEPTH, } from "./config";
|
|
114
|
+
export { getConfig, setConfig, createConfig, initConfig, DEVNET_CONFIG, DEVNET_REGTEST_CONFIG, MAINNET_CONFIG, LOCALNET_CONFIG, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, ATA_PROGRAM_ID, UTXOPIA_POLICY_PROGRAM_ID, SDK_VERSION, DEPLOYMENT_INFO, JOINSPLIT_TREE_DEPTH, } from "./config";
|
|
114
115
|
// ==========================================================================
|
|
115
116
|
// PoolState account layout + fee arithmetic
|
|
116
117
|
// ==========================================================================
|
|
@@ -126,7 +127,7 @@ poolStateSeeds, commitmentTreeSeeds, tokenConfigSeeds, poolConfigSeeds, nullifie
|
|
|
126
127
|
// ==========================================================================
|
|
127
128
|
// Stealth address utilities
|
|
128
129
|
// ==========================================================================
|
|
129
|
-
export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, createNonInteractiveDeposit, pickIkaCustodyKey, } from "./stealth";
|
|
130
|
+
export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, createTweakDeposit, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, createNonInteractiveDeposit, pickIkaCustodyKey, } from "./stealth";
|
|
130
131
|
// ==========================================================================
|
|
131
132
|
// PSBT builder for wallet-integrated deposits
|
|
132
133
|
// ==========================================================================
|
|
@@ -180,7 +181,7 @@ buildRotateTreeInstructionData, buildRotateTreeInstruction,
|
|
|
180
181
|
// MagicBlock lifecycle
|
|
181
182
|
buildMagicBlockDelegateInstructionData, buildMagicBlockDelegateInstruction, buildMagicBlockCommitInstructionData, buildMagicBlockCommitInstruction, buildMagicBlockPerPermissionInstructionData, buildMagicBlockPerPermissionInstruction, buildPolicyRequestHash, buildPolicyIntentParts, buildRegisterExitDestinationInstruction, buildRegisterExitDestinationInstructionData, MAX_POLICY_INTENT_PARTS, buildInitializePolicyApprovalInstructionData, buildInitializePolicyApprovalInstruction, buildPolicyApprovalDecisionInstruction, buildPolicyApprovalCommitInstruction, buildCompleteDepositPermissionedInstructionData, buildCompleteDepositPermissionedInstruction, buildShieldPermissionedInstructionData, buildShieldPermissionedInstruction, buildRotateAuditorInstructionData, buildRotateAuditorInstruction,
|
|
182
183
|
// Verify instruction data builders
|
|
183
|
-
buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData,
|
|
184
|
+
buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData, buildVerifyDepositInstructionData,
|
|
184
185
|
// Pool config (disc 2) builder + parser
|
|
185
186
|
buildSetPoolConfigInstructionData, parsePoolConfig, POOL_CONFIG_DISCRIMINATOR, POOL_CONFIG_LEN, POOL_SCRIPT_MAX_LEN, } from "./instructions";
|
|
186
187
|
// ==========================================================================
|
|
@@ -32,10 +32,13 @@ export declare const INSTRUCTION_DISCRIMINATORS: {
|
|
|
32
32
|
readonly CLAIM_FEES: 10;
|
|
33
33
|
readonly COMPLETE_DEPOSIT: 11;
|
|
34
34
|
readonly SHIELD: 12;
|
|
35
|
+
/** OP_RETURN-free deposit: note keys ride in instruction data, proven by the
|
|
36
|
+
* deposit address's Taproot tweak. */
|
|
37
|
+
readonly VERIFY_DEPOSIT: 25;
|
|
35
38
|
readonly TRANSACT: 13;
|
|
36
39
|
readonly UNSHIELD: 14;
|
|
37
40
|
readonly REDEEM: 15;
|
|
38
|
-
readonly
|
|
41
|
+
readonly FREEZE_VK_REGISTRY: 16;
|
|
39
42
|
readonly COMPLETE_REDEMPTION: 17;
|
|
40
43
|
readonly MARK_PROCESSING: 18;
|
|
41
44
|
readonly CANCEL_REDEMPTION: 19;
|
|
@@ -113,6 +116,17 @@ export interface CompleteRedemptionInstructionOptions {
|
|
|
113
116
|
poolVault: Address;
|
|
114
117
|
completionReceipt: Address;
|
|
115
118
|
poolConfig: Address;
|
|
119
|
+
/** HeightIndex PDA for the VerifiedTransaction's block —
|
|
120
|
+
* `deriveHeightIndexPDA(blockHeight, config.btcLightClientProgramId)`.
|
|
121
|
+
*
|
|
122
|
+
* REQUIRED. The program re-checks that the proof's block is still the canonical one at
|
|
123
|
+
* that height before it settles (audit_1 F-BTC-04): a VerifiedTransaction records a merkle
|
|
124
|
+
* proof that was valid once and is never invalidated, and the confirmation count is taken
|
|
125
|
+
* against a tip that only grows, so neither notices a reorg. Omitting this fails with
|
|
126
|
+
* InvalidSpvProof — the program locates the account by address, so its position in the
|
|
127
|
+
* list does not matter, but its absence is an error rather than a skipped check.
|
|
128
|
+
*/
|
|
129
|
+
heightIndex: Address;
|
|
116
130
|
/** Change UTXO PDA. Required when poolScript is non-empty. */
|
|
117
131
|
changeUtxo?: Address;
|
|
118
132
|
/** zkBTC TokenConfig PDA (credits protocol revenue) */
|
|
@@ -135,6 +149,17 @@ export interface ApproveRedemptionSigningInstructionOptions {
|
|
|
135
149
|
redemptionRequest: Address;
|
|
136
150
|
authority: Address;
|
|
137
151
|
poolConfig: Address;
|
|
152
|
+
/** HeightIndex PDA for the VerifiedTransaction's block —
|
|
153
|
+
* `deriveHeightIndexPDA(blockHeight, config.btcLightClientProgramId)`.
|
|
154
|
+
*
|
|
155
|
+
* REQUIRED. The program re-checks that the proof's block is still the canonical one at
|
|
156
|
+
* that height before it settles (audit_1 F-BTC-04): a VerifiedTransaction records a merkle
|
|
157
|
+
* proof that was valid once and is never invalidated, and the confirmation count is taken
|
|
158
|
+
* against a tip that only grows, so neither notices a reorg. Omitting this fails with
|
|
159
|
+
* InvalidSpvProof — the program locates the account by address, so its position in the
|
|
160
|
+
* list does not matter, but its absence is an error rather than a skipped check.
|
|
161
|
+
*/
|
|
162
|
+
heightIndex: Address;
|
|
138
163
|
ikaProgram: Address;
|
|
139
164
|
ikaCoordinator: Address;
|
|
140
165
|
ikaMessageApproval: Address;
|
|
@@ -732,6 +757,38 @@ export declare function buildCompleteDepositInstructionData(params: {
|
|
|
732
757
|
depositTxSize: number;
|
|
733
758
|
depositTxid: Uint8Array;
|
|
734
759
|
}): Uint8Array;
|
|
760
|
+
/**
|
|
761
|
+
* Build utxopia verify_deposit instruction data (disc=25).
|
|
762
|
+
*
|
|
763
|
+
* The OP_RETURN-free deposit path. `notePublicKey` + `ephemeralPubkey` travel in
|
|
764
|
+
* instruction data instead of in the Bitcoin transaction, and the program proves
|
|
765
|
+
* them against the deposit output's Taproot tweak — a different key pair derives
|
|
766
|
+
* a different address, which the funding transaction did not pay. Nothing marks
|
|
767
|
+
* the deposit as a UTXOpia transaction on chain, so any wallet or exchange that
|
|
768
|
+
* can send to a P2TR address can fund it.
|
|
769
|
+
*
|
|
770
|
+
* The address must be derived from `depositTweakCommitment(npk, eph)`, NOT from
|
|
771
|
+
* the note key alone — the program hashes both, so that a caller cannot swap in
|
|
772
|
+
* an ephemeral key that leaves the note undiscoverable.
|
|
773
|
+
*
|
|
774
|
+
* Sweep mode only: `depositTxSize` must be non-zero. The receipt PDA is seeded
|
|
775
|
+
* `["deposit_receipt", txid, vout]`, so pass `depositVout` to
|
|
776
|
+
* `deriveDepositReceiptPDA` for this flow.
|
|
777
|
+
*
|
|
778
|
+
* Layout: disc(1) + sweep_txid(32) + block_height(u64 LE) + sweep_tx_size(u32 LE)
|
|
779
|
+
* + deposit_tx_size(u32 LE) + deposit_txid(32) + ephemeral_pubkey(32)
|
|
780
|
+
* + note_public_key(32) + deposit_vout(u32 LE) = 149 bytes
|
|
781
|
+
*/
|
|
782
|
+
export declare function buildVerifyDepositInstructionData(params: {
|
|
783
|
+
sweepTxid: Uint8Array;
|
|
784
|
+
blockHeight: number;
|
|
785
|
+
sweepTxSize: number;
|
|
786
|
+
depositTxSize: number;
|
|
787
|
+
depositTxid: Uint8Array;
|
|
788
|
+
ephemeralPubkey: Uint8Array;
|
|
789
|
+
notePublicKey: Uint8Array;
|
|
790
|
+
depositVout: number;
|
|
791
|
+
}): Uint8Array;
|
|
735
792
|
/** PoolConfig account discriminator (0x0a) */
|
|
736
793
|
export declare const POOL_CONFIG_DISCRIMINATOR = 10;
|
|
737
794
|
/** Serialized PoolConfig account length (bytes) */
|
|
@@ -901,6 +958,17 @@ export interface CompleteDepositPermissionedOptions {
|
|
|
901
958
|
tokenConfig: Address;
|
|
902
959
|
/** 14. pool_config PDA (readonly) */
|
|
903
960
|
poolConfig: Address;
|
|
961
|
+
/** HeightIndex PDA for the VerifiedTransaction's block —
|
|
962
|
+
* `deriveHeightIndexPDA(blockHeight, config.btcLightClientProgramId)`.
|
|
963
|
+
*
|
|
964
|
+
* REQUIRED. The program re-checks that the proof's block is still the canonical one at
|
|
965
|
+
* that height before it settles (audit_1 F-BTC-04): a VerifiedTransaction records a merkle
|
|
966
|
+
* proof that was valid once and is never invalidated, and the confirmation count is taken
|
|
967
|
+
* against a tip that only grows, so neither notices a reorg. Omitting this fails with
|
|
968
|
+
* InvalidSpvProof — the program locates the account by address, so its position in the
|
|
969
|
+
* list does not matter, but its absence is an error rather than a skipped check.
|
|
970
|
+
*/
|
|
971
|
+
heightIndex: Address;
|
|
904
972
|
/** 15. one-time PolicyApproval (writable) */
|
|
905
973
|
policyApproval: Address;
|
|
906
974
|
};
|
|
@@ -944,6 +1012,8 @@ export declare function buildCompleteDepositPermissionedInstructionData(options:
|
|
|
944
1012
|
* 13. token_config (writable)
|
|
945
1013
|
* 14. pool_config (readonly)
|
|
946
1014
|
* 15. policy_approval (writable)
|
|
1015
|
+
* 16. policy_program (readonly)
|
|
1016
|
+
* 17. height_index (readonly) — canonicality re-check, located by address
|
|
947
1017
|
*/
|
|
948
1018
|
export declare function buildCompleteDepositPermissionedInstruction(options: CompleteDepositPermissionedOptions): Instruction;
|
|
949
1019
|
/** shieldPermissioned instruction options */
|
|
@@ -33,15 +33,19 @@ const INSTRUCTION = {
|
|
|
33
33
|
REGISTER_TOKEN: 8,
|
|
34
34
|
UPDATE_TOKEN_CONFIG: 9,
|
|
35
35
|
CLAIM_FEES: 10,
|
|
36
|
-
// Deposit (11-12)
|
|
36
|
+
// Deposit (11-12, 25)
|
|
37
37
|
COMPLETE_DEPOSIT: 11,
|
|
38
38
|
SHIELD: 12,
|
|
39
|
+
/** OP_RETURN-free deposit: note keys ride in instruction data, proven by the
|
|
40
|
+
* deposit address's Taproot tweak. */
|
|
41
|
+
VERIFY_DEPOSIT: 25,
|
|
39
42
|
// JoinSplit (13-15) — all share n_in + n_out + n_pub + proof_source header
|
|
40
43
|
TRANSACT: 13,
|
|
41
44
|
UNSHIELD: 14,
|
|
42
45
|
REDEEM: 15,
|
|
43
|
-
//
|
|
44
|
-
|
|
46
|
+
// VK registry freeze (16) — NOT part of the redemption range below
|
|
47
|
+
FREEZE_VK_REGISTRY: 16,
|
|
48
|
+
// Redemption lifecycle (17-19)
|
|
45
49
|
COMPLETE_REDEMPTION: 17,
|
|
46
50
|
MARK_PROCESSING: 18,
|
|
47
51
|
CANCEL_REDEMPTION: 19,
|
|
@@ -308,6 +312,9 @@ export function buildCompleteRedemptionInstruction(options) {
|
|
|
308
312
|
}
|
|
309
313
|
}
|
|
310
314
|
accounts.push({ address: options.accounts.tokenConfig, role: AccountRole.WRITABLE });
|
|
315
|
+
// Located by address, so the trailing position is free — this instruction already has a
|
|
316
|
+
// variable tail (change UTXO, consumed UTXOs) and the program scans rather than indexing.
|
|
317
|
+
accounts.push({ address: options.accounts.heightIndex, role: AccountRole.READONLY });
|
|
311
318
|
return {
|
|
312
319
|
programAddress: config.utxopiaProgramId,
|
|
313
320
|
accounts,
|
|
@@ -1297,6 +1304,67 @@ export function buildCompleteDepositInstructionData(params) {
|
|
|
1297
1304
|
return data;
|
|
1298
1305
|
}
|
|
1299
1306
|
// =============================================================================
|
|
1307
|
+
// UTXOpia Verify Deposit (disc=25) — OP_RETURN-free
|
|
1308
|
+
// =============================================================================
|
|
1309
|
+
/**
|
|
1310
|
+
* Build utxopia verify_deposit instruction data (disc=25).
|
|
1311
|
+
*
|
|
1312
|
+
* The OP_RETURN-free deposit path. `notePublicKey` + `ephemeralPubkey` travel in
|
|
1313
|
+
* instruction data instead of in the Bitcoin transaction, and the program proves
|
|
1314
|
+
* them against the deposit output's Taproot tweak — a different key pair derives
|
|
1315
|
+
* a different address, which the funding transaction did not pay. Nothing marks
|
|
1316
|
+
* the deposit as a UTXOpia transaction on chain, so any wallet or exchange that
|
|
1317
|
+
* can send to a P2TR address can fund it.
|
|
1318
|
+
*
|
|
1319
|
+
* The address must be derived from `depositTweakCommitment(npk, eph)`, NOT from
|
|
1320
|
+
* the note key alone — the program hashes both, so that a caller cannot swap in
|
|
1321
|
+
* an ephemeral key that leaves the note undiscoverable.
|
|
1322
|
+
*
|
|
1323
|
+
* Sweep mode only: `depositTxSize` must be non-zero. The receipt PDA is seeded
|
|
1324
|
+
* `["deposit_receipt", txid, vout]`, so pass `depositVout` to
|
|
1325
|
+
* `deriveDepositReceiptPDA` for this flow.
|
|
1326
|
+
*
|
|
1327
|
+
* Layout: disc(1) + sweep_txid(32) + block_height(u64 LE) + sweep_tx_size(u32 LE)
|
|
1328
|
+
* + deposit_tx_size(u32 LE) + deposit_txid(32) + ephemeral_pubkey(32)
|
|
1329
|
+
* + note_public_key(32) + deposit_vout(u32 LE) = 149 bytes
|
|
1330
|
+
*/
|
|
1331
|
+
export function buildVerifyDepositInstructionData(params) {
|
|
1332
|
+
for (const [name, value] of [
|
|
1333
|
+
["sweepTxid", params.sweepTxid],
|
|
1334
|
+
["depositTxid", params.depositTxid],
|
|
1335
|
+
["ephemeralPubkey", params.ephemeralPubkey],
|
|
1336
|
+
["notePublicKey", params.notePublicKey],
|
|
1337
|
+
]) {
|
|
1338
|
+
if (value.length !== 32) {
|
|
1339
|
+
throw new Error(`${name} must be 32 bytes, got ${value.length}`);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (params.depositTxSize === 0) {
|
|
1343
|
+
throw new Error("verify_deposit is sweep-mode only: depositTxSize must be non-zero");
|
|
1344
|
+
}
|
|
1345
|
+
const data = new Uint8Array(149);
|
|
1346
|
+
const view = new DataView(data.buffer);
|
|
1347
|
+
let offset = 0;
|
|
1348
|
+
data[offset++] = INSTRUCTION.VERIFY_DEPOSIT;
|
|
1349
|
+
data.set(params.sweepTxid, offset);
|
|
1350
|
+
offset += 32;
|
|
1351
|
+
view.setBigUint64(offset, BigInt(params.blockHeight), true);
|
|
1352
|
+
offset += 8;
|
|
1353
|
+
view.setUint32(offset, params.sweepTxSize, true);
|
|
1354
|
+
offset += 4;
|
|
1355
|
+
view.setUint32(offset, params.depositTxSize, true);
|
|
1356
|
+
offset += 4;
|
|
1357
|
+
data.set(params.depositTxid, offset);
|
|
1358
|
+
offset += 32;
|
|
1359
|
+
data.set(params.ephemeralPubkey, offset);
|
|
1360
|
+
offset += 32;
|
|
1361
|
+
data.set(params.notePublicKey, offset);
|
|
1362
|
+
offset += 32;
|
|
1363
|
+
view.setUint32(offset, params.depositVout, true);
|
|
1364
|
+
offset += 4;
|
|
1365
|
+
return data;
|
|
1366
|
+
}
|
|
1367
|
+
// =============================================================================
|
|
1300
1368
|
// UTXOpia Set Pool Config (disc=2)
|
|
1301
1369
|
// =============================================================================
|
|
1302
1370
|
/** PoolConfig account discriminator (0x0a) */
|
|
@@ -1523,6 +1591,8 @@ export function buildCompleteDepositPermissionedInstructionData(options) {
|
|
|
1523
1591
|
* 13. token_config (writable)
|
|
1524
1592
|
* 14. pool_config (readonly)
|
|
1525
1593
|
* 15. policy_approval (writable)
|
|
1594
|
+
* 16. policy_program (readonly)
|
|
1595
|
+
* 17. height_index (readonly) — canonicality re-check, located by address
|
|
1526
1596
|
*/
|
|
1527
1597
|
export function buildCompleteDepositPermissionedInstruction(options) {
|
|
1528
1598
|
const config = getConfig();
|
|
@@ -1554,6 +1624,7 @@ export function buildCompleteDepositPermissionedInstruction(options) {
|
|
|
1554
1624
|
{ address: options.accounts.poolConfig, role: AccountRole.READONLY },
|
|
1555
1625
|
{ address: options.accounts.policyApproval, role: AccountRole.WRITABLE },
|
|
1556
1626
|
{ address: config.policyProgramId ?? config.utxopiaProgramId, role: AccountRole.READONLY },
|
|
1627
|
+
{ address: options.accounts.heightIndex, role: AccountRole.READONLY },
|
|
1557
1628
|
],
|
|
1558
1629
|
data,
|
|
1559
1630
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "What am I proving?" — a canonical sentence for a shielded spend.
|
|
3
|
+
*
|
|
4
|
+
* The text is not the point. The point is that `renderSpendDoc` recomputes the
|
|
5
|
+
* proof's public signals from the numbers it is about to print and throws if
|
|
6
|
+
* they disagree, so a UI cannot caption a proof with an amount or a destination
|
|
7
|
+
* the proof does not actually contain.
|
|
8
|
+
*
|
|
9
|
+
* Nothing here changes the circuit. Every value below is already bound to the
|
|
10
|
+
* user's spending key by the in-circuit EdDSA over
|
|
11
|
+
* Poseidon(merkleRoot, boundParamsHash, nullifiers.., commitmentsOut..).
|
|
12
|
+
*/
|
|
13
|
+
import { type SolanaPrivacyDomainContext } from "./bound-params";
|
|
14
|
+
export interface SpendDoc {
|
|
15
|
+
mode: "transfer" | "unshield" | "redeem";
|
|
16
|
+
/** Display label, e.g. "Solana Devnet". */
|
|
17
|
+
network: string;
|
|
18
|
+
/** Display label, e.g. "zkBTC". */
|
|
19
|
+
asset: string;
|
|
20
|
+
decimals: number;
|
|
21
|
+
/** Destination as shown to the user (a .sol name, a BTC address, a pubkey). */
|
|
22
|
+
recipient: string;
|
|
23
|
+
/**
|
|
24
|
+
* The destination bytes actually folded into boundParamsHash: 32-byte Solana
|
|
25
|
+
* owner for `unshield`, raw scriptPubKey for `redeem`. Omitted for `transfer`,
|
|
26
|
+
* where the destination is private and provably absent from the signals.
|
|
27
|
+
*/
|
|
28
|
+
recipientBytes?: Uint8Array;
|
|
29
|
+
/** Raw units the recipient receives. */
|
|
30
|
+
amount: bigint;
|
|
31
|
+
relayerFee: bigint;
|
|
32
|
+
change: bigint;
|
|
33
|
+
}
|
|
34
|
+
/** The public signals of the proof about to be generated, plus what built them. */
|
|
35
|
+
export interface SpendSignals {
|
|
36
|
+
/** `outputs.map(o => o.value)` from JoinSplitProofInputs. */
|
|
37
|
+
outputValues: bigint[];
|
|
38
|
+
/** The boundParamsHash going into the proof. */
|
|
39
|
+
boundParamsHash: bigint;
|
|
40
|
+
stealthDataHash: Uint8Array;
|
|
41
|
+
chainId: bigint;
|
|
42
|
+
domain: SolanaPrivacyDomainContext;
|
|
43
|
+
/** Redeem only: the on-chain requester bound into the proof. */
|
|
44
|
+
requester?: Uint8Array;
|
|
45
|
+
treeNumber?: number;
|
|
46
|
+
}
|
|
47
|
+
export declare class SpendDocMismatch extends Error {
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The statement itself. Show this before the user commits; pass the same `doc`
|
|
51
|
+
* to `renderSpendDoc` when the proof is built so the string they read is the
|
|
52
|
+
* string that gets checked.
|
|
53
|
+
*/
|
|
54
|
+
export declare function formatSpendDoc(doc: SpendDoc): string;
|
|
55
|
+
/**
|
|
56
|
+
* Render the statement, or throw if it does not describe `signals`.
|
|
57
|
+
*
|
|
58
|
+
* Checked: every amount on screen is an output value of the proof and there are
|
|
59
|
+
* no other outputs; the destination reproduces boundParamsHash.
|
|
60
|
+
* Not checked: that `recipient` (a label) names `recipientBytes` — the doc
|
|
61
|
+
* prints the bound bytes so that stays verifiable by eye.
|
|
62
|
+
*/
|
|
63
|
+
export declare function renderSpendDoc(doc: SpendDoc, signals: SpendSignals): string;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "What am I proving?" — a canonical sentence for a shielded spend.
|
|
3
|
+
*
|
|
4
|
+
* The text is not the point. The point is that `renderSpendDoc` recomputes the
|
|
5
|
+
* proof's public signals from the numbers it is about to print and throws if
|
|
6
|
+
* they disagree, so a UI cannot caption a proof with an amount or a destination
|
|
7
|
+
* the proof does not actually contain.
|
|
8
|
+
*
|
|
9
|
+
* Nothing here changes the circuit. Every value below is already bound to the
|
|
10
|
+
* user's spending key by the in-circuit EdDSA over
|
|
11
|
+
* Poseidon(merkleRoot, boundParamsHash, nullifiers.., commitmentsOut..).
|
|
12
|
+
*/
|
|
13
|
+
import { computeSolanaDomainBoundParamsHash, createRedeemBoundParams, createTransferBoundParams, createUnshieldBoundParams, } from "./bound-params";
|
|
14
|
+
export class SpendDocMismatch extends Error {
|
|
15
|
+
}
|
|
16
|
+
function fmt(raw, decimals) {
|
|
17
|
+
const neg = raw < 0n;
|
|
18
|
+
const s = (neg ? -raw : raw).toString().padStart(decimals + 1, "0");
|
|
19
|
+
const whole = s.slice(0, s.length - decimals);
|
|
20
|
+
const frac = decimals === 0 ? "" : s.slice(s.length - decimals).replace(/0+$/, "");
|
|
21
|
+
return `${neg ? "-" : ""}${whole}${frac ? `.${frac}` : ""}`;
|
|
22
|
+
}
|
|
23
|
+
const hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
|
|
24
|
+
function sortedValues(v) {
|
|
25
|
+
return [...v].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)).join(",");
|
|
26
|
+
}
|
|
27
|
+
function expectedBoundParamsHash(doc, s) {
|
|
28
|
+
const tree = s.treeNumber ?? 0;
|
|
29
|
+
switch (doc.mode) {
|
|
30
|
+
case "transfer":
|
|
31
|
+
return computeSolanaDomainBoundParamsHash(createTransferBoundParams(s.stealthDataHash, s.chainId, tree), s.domain);
|
|
32
|
+
case "unshield":
|
|
33
|
+
if (!doc.recipientBytes)
|
|
34
|
+
throw new SpendDocMismatch("unshield doc has no recipientBytes");
|
|
35
|
+
return computeSolanaDomainBoundParamsHash(createUnshieldBoundParams(doc.recipientBytes, s.stealthDataHash, s.chainId, tree), s.domain);
|
|
36
|
+
case "redeem":
|
|
37
|
+
if (!doc.recipientBytes)
|
|
38
|
+
throw new SpendDocMismatch("redeem doc has no recipientBytes");
|
|
39
|
+
if (!s.requester)
|
|
40
|
+
throw new SpendDocMismatch("redeem doc has no requester");
|
|
41
|
+
return computeSolanaDomainBoundParamsHash(createRedeemBoundParams(doc.recipientBytes, s.stealthDataHash, s.requester, s.chainId, tree), s.domain);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The statement itself. Show this before the user commits; pass the same `doc`
|
|
46
|
+
* to `renderSpendDoc` when the proof is built so the string they read is the
|
|
47
|
+
* string that gets checked.
|
|
48
|
+
*/
|
|
49
|
+
export function formatSpendDoc(doc) {
|
|
50
|
+
const amt = (v) => `${fmt(v, doc.decimals)} ${doc.asset}`;
|
|
51
|
+
const action = doc.mode === "redeem"
|
|
52
|
+
? `Withdraw ${amt(doc.amount)} to Bitcoin`
|
|
53
|
+
: doc.mode === "unshield"
|
|
54
|
+
? `Unshield ${amt(doc.amount)}`
|
|
55
|
+
: `Send ${amt(doc.amount)} privately`;
|
|
56
|
+
const lines = [
|
|
57
|
+
"UTXOpia Proof",
|
|
58
|
+
"",
|
|
59
|
+
"I AM PROVING",
|
|
60
|
+
action,
|
|
61
|
+
"",
|
|
62
|
+
"DETAILS",
|
|
63
|
+
`Network: ${doc.network}`,
|
|
64
|
+
`Amount leaving the pool: ${amt(doc.amount)}`,
|
|
65
|
+
`To: ${doc.recipient}`,
|
|
66
|
+
];
|
|
67
|
+
if (doc.recipientBytes)
|
|
68
|
+
lines.push(`Bound destination: ${hex(doc.recipientBytes)}`);
|
|
69
|
+
if (doc.relayerFee > 0n)
|
|
70
|
+
lines.push(`Relayer fee: ${amt(doc.relayerFee)}`);
|
|
71
|
+
if (doc.change > 0n)
|
|
72
|
+
lines.push(`Change back to me: ${amt(doc.change)}`);
|
|
73
|
+
lines.push("", "ENFORCED BY THE PROOF", "Amounts: these are every output this proof creates", doc.mode === "transfer"
|
|
74
|
+
? "Destination: private — not in the public signals. Confirm it with the recipient."
|
|
75
|
+
: "Destination: bound into boundParamsHash and re-derived onchain");
|
|
76
|
+
if (doc.mode !== "transfer") {
|
|
77
|
+
lines.push("Protocol fee: deducted onchain from pool policy, not part of this statement");
|
|
78
|
+
}
|
|
79
|
+
lines.push("", "Protocol: utxopia-spend-doc-v1");
|
|
80
|
+
return lines.join("\n");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Render the statement, or throw if it does not describe `signals`.
|
|
84
|
+
*
|
|
85
|
+
* Checked: every amount on screen is an output value of the proof and there are
|
|
86
|
+
* no other outputs; the destination reproduces boundParamsHash.
|
|
87
|
+
* Not checked: that `recipient` (a label) names `recipientBytes` — the doc
|
|
88
|
+
* prints the bound bytes so that stays verifiable by eye.
|
|
89
|
+
*/
|
|
90
|
+
export function renderSpendDoc(doc, signals) {
|
|
91
|
+
const shown = [doc.amount, doc.relayerFee, doc.change].filter((v) => v > 0n);
|
|
92
|
+
if (sortedValues(shown) !== sortedValues(signals.outputValues)) {
|
|
93
|
+
throw new SpendDocMismatch(`amounts do not match the proof outputs: doc [${sortedValues(shown)}] vs proof [${sortedValues(signals.outputValues)}]`);
|
|
94
|
+
}
|
|
95
|
+
const expected = expectedBoundParamsHash(doc, signals);
|
|
96
|
+
if (expected !== signals.boundParamsHash) {
|
|
97
|
+
throw new SpendDocMismatch("destination does not match the proof's boundParamsHash");
|
|
98
|
+
}
|
|
99
|
+
return formatSpendDoc(doc);
|
|
100
|
+
}
|
|
@@ -200,6 +200,34 @@ export declare function createNonInteractiveDeposit(recipientMeta: StealthMetaAd
|
|
|
200
200
|
* credits the note from that transaction.
|
|
201
201
|
*/
|
|
202
202
|
export declare function createDirectVaultDeposit(recipientMeta: StealthMetaAddress, vaultXOnlyPubkey: Uint8Array, network?: "mainnet" | "testnet" | "regtest", opReturnContext?: DepositOpReturnContext): Promise<NonInteractiveDepositResult>;
|
|
203
|
+
/** A deposit whose address alone binds the note keys — no OP_RETURN. */
|
|
204
|
+
export interface TweakDepositResult {
|
|
205
|
+
/** Taproot address to send BTC to */
|
|
206
|
+
btcAddress: string;
|
|
207
|
+
/** 32-byte x-only output key for the deposit P2TR output */
|
|
208
|
+
depositOutputKey: Uint8Array;
|
|
209
|
+
/** 32-byte note public key */
|
|
210
|
+
npk: Uint8Array;
|
|
211
|
+
/** 32-byte Ed25519 ephemeral public key */
|
|
212
|
+
ephemeralPub: Uint8Array;
|
|
213
|
+
/** sha256(npk || ephemeralPub) — the commitment the address is tweaked by */
|
|
214
|
+
tweakCommitment: Uint8Array;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Create a deposit for the OP_RETURN-free flow (`verify_deposit`, disc 25).
|
|
218
|
+
*
|
|
219
|
+
* The transaction carries nothing but a payment, so anything that can send to a
|
|
220
|
+
* P2TR address can fund it — a hardware wallet, an exchange withdrawal, a faucet.
|
|
221
|
+
* The note keys are recovered from instruction data at completion time and proven
|
|
222
|
+
* against this address's Taproot tweak, so substituting either key derives a
|
|
223
|
+
* different address that the funding transaction never paid.
|
|
224
|
+
*
|
|
225
|
+
* Sweep mode: the pool sweeps this address into its own custody, and that sweep
|
|
226
|
+
* is what gets SPV-verified. Register the address with the tracker BEFORE any
|
|
227
|
+
* coins are sent — a deposit with no OP_RETURN is invisible to block scanning,
|
|
228
|
+
* so an unregistered address is one nobody is watching.
|
|
229
|
+
*/
|
|
230
|
+
export declare function createTweakDeposit(recipientMeta: StealthMetaAddress, vaultXOnlyPubkey: Uint8Array, network?: "mainnet" | "testnet" | "regtest"): Promise<TweakDepositResult>;
|
|
203
231
|
/**
|
|
204
232
|
* Create a non-interactive deposit using the current SDK config.
|
|
205
233
|
*
|
|
@@ -248,6 +248,42 @@ export async function createDirectVaultDeposit(recipientMeta, vaultXOnlyPubkey,
|
|
|
248
248
|
ephemeralPub,
|
|
249
249
|
};
|
|
250
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Create a deposit for the OP_RETURN-free flow (`verify_deposit`, disc 25).
|
|
253
|
+
*
|
|
254
|
+
* The transaction carries nothing but a payment, so anything that can send to a
|
|
255
|
+
* P2TR address can fund it — a hardware wallet, an exchange withdrawal, a faucet.
|
|
256
|
+
* The note keys are recovered from instruction data at completion time and proven
|
|
257
|
+
* against this address's Taproot tweak, so substituting either key derives a
|
|
258
|
+
* different address that the funding transaction never paid.
|
|
259
|
+
*
|
|
260
|
+
* Sweep mode: the pool sweeps this address into its own custody, and that sweep
|
|
261
|
+
* is what gets SPV-verified. Register the address with the tracker BEFORE any
|
|
262
|
+
* coins are sent — a deposit with no OP_RETURN is invisible to block scanning,
|
|
263
|
+
* so an unregistered address is one nobody is watching.
|
|
264
|
+
*/
|
|
265
|
+
export async function createTweakDeposit(recipientMeta, vaultXOnlyPubkey, network = "testnet") {
|
|
266
|
+
if (vaultXOnlyPubkey.length !== 32) {
|
|
267
|
+
throw new Error("vaultXOnlyPubkey must be 32 bytes");
|
|
268
|
+
}
|
|
269
|
+
const viewingPubKey = new Uint8Array(recipientMeta.viewingPubKey);
|
|
270
|
+
const ephemeral = ed25519GenerateKeyPair();
|
|
271
|
+
const sharedSecret = x25519Ecdh(ephemeral.privKey, viewingPubKey);
|
|
272
|
+
const stealthScalar = deriveStealthScalar(sharedSecret);
|
|
273
|
+
const recipientMPK = bytesToBigint(recipientMeta.mpk);
|
|
274
|
+
const npk = bigintToBytes(computeNPKSync(recipientMPK, stealthScalar));
|
|
275
|
+
const ephemeralPub = new Uint8Array(ephemeral.pubKey);
|
|
276
|
+
const { depositTweakCommitment, deriveTaprootAddress } = await import("./taproot");
|
|
277
|
+
const tweakCommitment = depositTweakCommitment(npk, ephemeralPub);
|
|
278
|
+
const { address, outputKey } = deriveTaprootAddress(tweakCommitment, network, vaultXOnlyPubkey);
|
|
279
|
+
return {
|
|
280
|
+
btcAddress: address,
|
|
281
|
+
depositOutputKey: outputKey,
|
|
282
|
+
npk,
|
|
283
|
+
ephemeralPub,
|
|
284
|
+
tweakCommitment,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
251
287
|
/**
|
|
252
288
|
* Create a non-interactive deposit using the current SDK config.
|
|
253
289
|
*
|
|
@@ -23,6 +23,17 @@ export declare function deriveTaprootAddress(commitment: Uint8Array, network?: "
|
|
|
23
23
|
outputKey: Uint8Array;
|
|
24
24
|
tweak: Uint8Array;
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Commitment a `verify_deposit` (disc 25) deposit address is derived from.
|
|
28
|
+
*
|
|
29
|
+
* Both keys are hashed in. Binding the note key alone would leave the ephemeral
|
|
30
|
+
* pubkey caller-chosen on the Solana side: the credited amount and owner would
|
|
31
|
+
* still be right, but a substituted ephemeral key makes the stealth announcement
|
|
32
|
+
* undecryptable and the recipient never finds their note.
|
|
33
|
+
*
|
|
34
|
+
* Feed the result to `deriveTaprootAddress` as the commitment.
|
|
35
|
+
*/
|
|
36
|
+
export declare function depositTweakCommitment(notePublicKey: Uint8Array, ephemeralPubkey: Uint8Array): Uint8Array;
|
|
26
37
|
/**
|
|
27
38
|
* Verify that a Taproot address is correctly derived from a commitment
|
|
28
39
|
*
|
|
@@ -70,6 +70,25 @@ export function deriveTaprootAddress(commitment, network = "testnet", internalKe
|
|
|
70
70
|
tweak,
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Commitment a `verify_deposit` (disc 25) deposit address is derived from.
|
|
75
|
+
*
|
|
76
|
+
* Both keys are hashed in. Binding the note key alone would leave the ephemeral
|
|
77
|
+
* pubkey caller-chosen on the Solana side: the credited amount and owner would
|
|
78
|
+
* still be right, but a substituted ephemeral key makes the stealth announcement
|
|
79
|
+
* undecryptable and the recipient never finds their note.
|
|
80
|
+
*
|
|
81
|
+
* Feed the result to `deriveTaprootAddress` as the commitment.
|
|
82
|
+
*/
|
|
83
|
+
export function depositTweakCommitment(notePublicKey, ephemeralPubkey) {
|
|
84
|
+
if (notePublicKey.length !== 32 || ephemeralPubkey.length !== 32) {
|
|
85
|
+
throw new Error("notePublicKey and ephemeralPubkey must be 32 bytes");
|
|
86
|
+
}
|
|
87
|
+
const material = new Uint8Array(64);
|
|
88
|
+
material.set(notePublicKey, 0);
|
|
89
|
+
material.set(ephemeralPubkey, 32);
|
|
90
|
+
return sha256(material);
|
|
91
|
+
}
|
|
73
92
|
/**
|
|
74
93
|
* Verify that a Taproot address is correctly derived from a commitment
|
|
75
94
|
*
|