privacycash 1.1.16 → 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deposit.d.ts +1 -1
- package/dist/deposit.js +195 -175
- package/dist/depositSPL.d.ts +1 -1
- package/dist/depositSPL.js +194 -174
- package/dist/withdraw.d.ts +1 -1
- package/dist/withdraw.js +134 -113
- package/dist/withdrawSPL.d.ts +1 -1
- package/dist/withdrawSPL.js +135 -115
- package/package.json +1 -1
- package/src/deposit.ts +221 -202
- package/src/depositSPL.ts +219 -201
- package/src/withdraw.ts +156 -134
- package/src/withdrawSPL.ts +158 -136
package/dist/deposit.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Connection, PublicKey, VersionedTransaction } from '@solana/web3.js';
|
|
2
1
|
import * as hasher from '@lightprotocol/hasher.rs';
|
|
2
|
+
import { Connection, PublicKey, VersionedTransaction } from '@solana/web3.js';
|
|
3
3
|
import { EncryptionService } from './utils/encryption.js';
|
|
4
4
|
type DepositParams = {
|
|
5
5
|
publicKey: PublicKey;
|
package/dist/deposit.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ComputeBudgetProgram, LAMPORTS_PER_SOL, PublicKey, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js';
|
|
2
2
|
import BN from 'bn.js';
|
|
3
|
-
import { Utxo } from './models/utxo.js';
|
|
4
|
-
import { fetchMerkleProof, findNullifierPDAs, getExtDataHash, getProgramAccounts, queryRemoteTreeState, findCrossCheckNullifierPDAs } from './utils/utils.js';
|
|
5
|
-
import { prove, parseProofToBytesArray, parseToBytesArray } from './utils/prover.js';
|
|
6
|
-
import { MerkleTree } from './utils/merkle_tree.js';
|
|
7
|
-
import { serializeProofAndExtData } from './utils/encryption.js';
|
|
8
|
-
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
9
3
|
import { getUtxos } from './getUtxos.js';
|
|
10
|
-
import {
|
|
4
|
+
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
5
|
+
import { Utxo } from './models/utxo.js';
|
|
11
6
|
import { useExistingALT } from './utils/address_lookup_table.js';
|
|
7
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, MERKLE_TREE_DEPTH, PROGRAM_ID, RELAYER_API_URL } from './utils/constants.js';
|
|
8
|
+
import { serializeProofAndExtData } from './utils/encryption.js';
|
|
12
9
|
import { logger } from './utils/logger.js';
|
|
10
|
+
import { MerkleTree } from './utils/merkle_tree.js';
|
|
11
|
+
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
12
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getProgramAccounts, queryRemoteTreeState } from './utils/utils.js';
|
|
13
13
|
// Function to relay pre-signed deposit transaction to indexer backend
|
|
14
14
|
async function relayDepositToIndexer(signedTransaction, publicKey, referrer) {
|
|
15
15
|
try {
|
|
@@ -68,10 +68,6 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
68
68
|
const { treeAccount, treeTokenAccount, globalConfigAccount } = getProgramAccounts();
|
|
69
69
|
// Create the merkle tree with the pre-initialized poseidon hash
|
|
70
70
|
const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
|
|
71
|
-
// Initialize root and nextIndex variables
|
|
72
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState();
|
|
73
|
-
logger.debug(`Using tree root: ${root}`);
|
|
74
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
75
71
|
// Generate a deterministic private key derived from the wallet keypair
|
|
76
72
|
// const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
77
73
|
const utxoPrivateKey = encryptionService.getUtxoPrivateKeyV2();
|
|
@@ -88,177 +84,201 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
88
84
|
let inputs;
|
|
89
85
|
let inputMerklePathIndices;
|
|
90
86
|
let inputMerklePathElements;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
logger.debug(`
|
|
98
|
-
logger.debug(`
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
let getProveRetryCount = 0;
|
|
88
|
+
let extData;
|
|
89
|
+
let encryptedOutput1;
|
|
90
|
+
let getProve = async () => {
|
|
91
|
+
// Initialize root and nextIndex variables
|
|
92
|
+
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState();
|
|
93
|
+
logger.debug(`Using tree root: ${root}`);
|
|
94
|
+
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
95
|
+
if (existingUnspentUtxos.length === 0) {
|
|
96
|
+
// Scenario 1: Fresh deposit with dummy inputs - add new funds to the system
|
|
97
|
+
extAmount = amount_in_lamports;
|
|
98
|
+
outputAmount = new BN(amount_in_lamports).sub(new BN(fee_amount_in_lamports)).toString();
|
|
99
|
+
logger.debug(`Fresh deposit scenario (no existing UTXOs):`);
|
|
100
|
+
logger.debug(`External amount (deposit): ${extAmount}`);
|
|
101
|
+
logger.debug(`Fee amount: ${fee_amount_in_lamports}`);
|
|
102
|
+
logger.debug(`Output amount: ${outputAmount}`);
|
|
103
|
+
// Use two dummy UTXOs as inputs
|
|
104
|
+
inputs = [
|
|
105
|
+
new Utxo({
|
|
106
|
+
lightWasm,
|
|
107
|
+
keypair: utxoKeypair
|
|
108
|
+
}),
|
|
109
|
+
new Utxo({
|
|
110
|
+
lightWasm,
|
|
111
|
+
keypair: utxoKeypair
|
|
112
|
+
})
|
|
113
|
+
];
|
|
114
|
+
// Both inputs are dummy, so use mock indices and zero-filled Merkle paths
|
|
115
|
+
inputMerklePathIndices = inputs.map((input) => input.index || 0);
|
|
116
|
+
inputMerklePathElements = inputs.map(() => {
|
|
117
|
+
return [...new Array(tree.levels).fill("0")];
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
// Scenario 2: Deposit that consolidates with existing UTXO(s)
|
|
122
|
+
const firstUtxo = existingUnspentUtxos[0];
|
|
123
|
+
const firstUtxoAmount = firstUtxo.amount;
|
|
124
|
+
const secondUtxoAmount = existingUnspentUtxos.length > 1 ? existingUnspentUtxos[1].amount : new BN(0);
|
|
125
|
+
extAmount = amount_in_lamports; // Still depositing new funds
|
|
126
|
+
// Output combines existing UTXO amounts + new deposit amount - fee
|
|
127
|
+
outputAmount = firstUtxoAmount.add(secondUtxoAmount).add(new BN(amount_in_lamports)).sub(new BN(fee_amount_in_lamports)).toString();
|
|
128
|
+
logger.debug(`Deposit with consolidation scenario:`);
|
|
129
|
+
logger.debug(`First existing UTXO amount: ${firstUtxoAmount.toString()}`);
|
|
130
|
+
if (secondUtxoAmount.gt(new BN(0))) {
|
|
131
|
+
logger.debug(`Second existing UTXO amount: ${secondUtxoAmount.toString()}`);
|
|
132
|
+
}
|
|
133
|
+
logger.debug(`New deposit amount: ${amount_in_lamports}`);
|
|
134
|
+
logger.debug(`Fee amount: ${fee_amount_in_lamports}`);
|
|
135
|
+
logger.debug(`Output amount (existing UTXOs + deposit - fee): ${outputAmount}`);
|
|
136
|
+
logger.debug(`External amount (deposit): ${extAmount}`);
|
|
137
|
+
logger.debug('\nFirst UTXO to be consolidated:');
|
|
138
|
+
await firstUtxo.log();
|
|
139
|
+
// Use first existing UTXO as first input, and either second UTXO or dummy UTXO as second input
|
|
140
|
+
const secondUtxo = existingUnspentUtxos.length > 1 ? existingUnspentUtxos[1] : new Utxo({
|
|
141
|
+
lightWasm,
|
|
142
|
+
keypair: utxoKeypair,
|
|
143
|
+
amount: '0'
|
|
144
|
+
});
|
|
145
|
+
inputs = [
|
|
146
|
+
firstUtxo, // Use the first existing UTXO
|
|
147
|
+
secondUtxo // Use second UTXO if available, otherwise dummy
|
|
148
|
+
];
|
|
149
|
+
// Fetch Merkle proofs for real UTXOs
|
|
150
|
+
const firstUtxoCommitment = await firstUtxo.getCommitment();
|
|
151
|
+
const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment);
|
|
152
|
+
let secondUtxoMerkleProof;
|
|
153
|
+
if (secondUtxo.amount.gt(new BN(0))) {
|
|
154
|
+
// Second UTXO is real, fetch its proof
|
|
155
|
+
const secondUtxoCommitment = await secondUtxo.getCommitment();
|
|
156
|
+
secondUtxoMerkleProof = await fetchMerkleProof(secondUtxoCommitment);
|
|
157
|
+
logger.debug('\nSecond UTXO to be consolidated:');
|
|
158
|
+
await secondUtxo.log();
|
|
159
|
+
}
|
|
160
|
+
// Use the real pathIndices from API for real inputs, mock index for dummy input
|
|
161
|
+
inputMerklePathIndices = [
|
|
162
|
+
firstUtxo.index || 0, // Use the real UTXO's index
|
|
163
|
+
secondUtxo.amount.gt(new BN(0)) ? (secondUtxo.index || 0) : 0 // Real UTXO index or dummy
|
|
164
|
+
];
|
|
165
|
+
// Create Merkle path elements: real proof for real inputs, zeros for dummy input
|
|
166
|
+
inputMerklePathElements = [
|
|
167
|
+
firstUtxoMerkleProof.pathElements, // Real Merkle proof for first existing UTXO
|
|
168
|
+
secondUtxo.amount.gt(new BN(0)) ? secondUtxoMerkleProof.pathElements : [...new Array(tree.levels).fill("0")] // Real proof or zero-filled for dummy
|
|
169
|
+
];
|
|
170
|
+
logger.debug(`Using first UTXO with amount: ${firstUtxo.amount.toString()} and index: ${firstUtxo.index}`);
|
|
171
|
+
logger.debug(`Using second ${secondUtxo.amount.gt(new BN(0)) ? 'UTXO' : 'dummy UTXO'} with amount: ${secondUtxo.amount.toString()}${secondUtxo.amount.gt(new BN(0)) ? ` and index: ${secondUtxo.index}` : ''}`);
|
|
172
|
+
logger.debug(`First UTXO Merkle proof path indices from API: [${firstUtxoMerkleProof.pathIndices.join(', ')}]`);
|
|
173
|
+
if (secondUtxo.amount.gt(new BN(0))) {
|
|
174
|
+
logger.debug(`Second UTXO Merkle proof path indices from API: [${secondUtxoMerkleProof.pathIndices.join(', ')}]`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const publicAmountForCircuit = new BN(extAmount).sub(new BN(fee_amount_in_lamports)).add(FIELD_SIZE).mod(FIELD_SIZE);
|
|
178
|
+
logger.debug(`Public amount calculation: (${extAmount} - ${fee_amount_in_lamports} + FIELD_SIZE) % FIELD_SIZE = ${publicAmountForCircuit.toString()}`);
|
|
179
|
+
// Create outputs for the transaction with the same shared keypair
|
|
180
|
+
const outputs = [
|
|
101
181
|
new Utxo({
|
|
102
182
|
lightWasm,
|
|
103
|
-
|
|
104
|
-
|
|
183
|
+
amount: outputAmount,
|
|
184
|
+
keypair: utxoKeypair,
|
|
185
|
+
index: currentNextIndex // This UTXO will be inserted at currentNextIndex
|
|
186
|
+
}), // Output with value (either deposit amount minus fee, or input amount minus fee)
|
|
105
187
|
new Utxo({
|
|
106
188
|
lightWasm,
|
|
107
|
-
|
|
108
|
-
|
|
189
|
+
amount: '0',
|
|
190
|
+
keypair: utxoKeypair,
|
|
191
|
+
index: currentNextIndex + 1 // This UTXO will be inserted at currentNextIndex + 1
|
|
192
|
+
}) // Empty UTXO
|
|
109
193
|
];
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
logger.debug(
|
|
125
|
-
logger.debug(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
logger.debug(
|
|
131
|
-
|
|
132
|
-
logger.debug(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
];
|
|
145
|
-
//
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
194
|
+
// Verify this matches the circuit balance equation: sumIns + publicAmount = sumOuts
|
|
195
|
+
const sumIns = inputs.reduce((sum, input) => sum.add(input.amount), new BN(0));
|
|
196
|
+
const sumOuts = outputs.reduce((sum, output) => sum.add(output.amount), new BN(0));
|
|
197
|
+
logger.debug(`Circuit balance check: sumIns(${sumIns.toString()}) + publicAmount(${publicAmountForCircuit.toString()}) should equal sumOuts(${sumOuts.toString()})`);
|
|
198
|
+
// Convert to circuit-compatible format
|
|
199
|
+
const publicAmountCircuitResult = sumIns.add(publicAmountForCircuit).mod(FIELD_SIZE);
|
|
200
|
+
logger.debug(`Balance verification: ${sumIns.toString()} + ${publicAmountForCircuit.toString()} (mod FIELD_SIZE) = ${publicAmountCircuitResult.toString()}`);
|
|
201
|
+
logger.debug(`Expected sum of outputs: ${sumOuts.toString()}`);
|
|
202
|
+
logger.debug(`Balance equation satisfied: ${publicAmountCircuitResult.eq(sumOuts)}`);
|
|
203
|
+
// Generate nullifiers and commitments
|
|
204
|
+
const inputNullifiers = await Promise.all(inputs.map(x => x.getNullifier()));
|
|
205
|
+
const outputCommitments = await Promise.all(outputs.map(x => x.getCommitment()));
|
|
206
|
+
// Save original commitment and nullifier values for verification
|
|
207
|
+
logger.debug('\n=== UTXO VALIDATION ===');
|
|
208
|
+
logger.debug('Output 0 Commitment:', outputCommitments[0]);
|
|
209
|
+
logger.debug('Output 1 Commitment:', outputCommitments[1]);
|
|
210
|
+
// Encrypt the UTXO data using a compact format that includes the keypair
|
|
211
|
+
logger.debug('\nEncrypting UTXOs with keypair data...');
|
|
212
|
+
encryptedOutput1 = encryptionService.encryptUtxo(outputs[0]);
|
|
213
|
+
const encryptedOutput2 = encryptionService.encryptUtxo(outputs[1]);
|
|
214
|
+
logger.debug(`\nOutput[0] (with value):`);
|
|
215
|
+
await outputs[0].log();
|
|
216
|
+
logger.debug(`\nOutput[1] (empty):`);
|
|
217
|
+
await outputs[1].log();
|
|
218
|
+
logger.debug(`\nEncrypted output 1 size: ${encryptedOutput1.length} bytes`);
|
|
219
|
+
logger.debug(`Encrypted output 2 size: ${encryptedOutput2.length} bytes`);
|
|
220
|
+
logger.debug(`Total encrypted outputs size: ${encryptedOutput1.length + encryptedOutput2.length} bytes`);
|
|
221
|
+
// Test decryption to verify commitment values match
|
|
222
|
+
logger.debug('\n=== TESTING DECRYPTION ===');
|
|
223
|
+
logger.debug('Decrypting output 1 to verify commitment matches...');
|
|
224
|
+
const decryptedUtxo1 = await encryptionService.decryptUtxo(encryptedOutput1, lightWasm);
|
|
225
|
+
const decryptedCommitment1 = await decryptedUtxo1.getCommitment();
|
|
226
|
+
logger.debug('Original commitment:', outputCommitments[0]);
|
|
227
|
+
logger.debug('Decrypted commitment:', decryptedCommitment1);
|
|
228
|
+
logger.debug('Commitment matches:', outputCommitments[0] === decryptedCommitment1);
|
|
229
|
+
// Create the deposit ExtData with real encrypted outputs
|
|
230
|
+
extData = {
|
|
231
|
+
// recipient - just a placeholder, not actually used for deposits.
|
|
232
|
+
recipient: new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM'),
|
|
233
|
+
extAmount: new BN(extAmount),
|
|
234
|
+
encryptedOutput1: encryptedOutput1,
|
|
235
|
+
encryptedOutput2: encryptedOutput2,
|
|
236
|
+
fee: new BN(fee_amount_in_lamports),
|
|
237
|
+
feeRecipient: FEE_RECIPIENT,
|
|
238
|
+
mintAddress: inputs[0].mintAddress
|
|
239
|
+
};
|
|
240
|
+
// Calculate the extDataHash with the encrypted outputs (now includes mintAddress for security)
|
|
241
|
+
const calculatedExtDataHash = getExtDataHash(extData);
|
|
242
|
+
// Create the input for the proof generation (must match circuit input order exactly)
|
|
243
|
+
const input = {
|
|
244
|
+
// Common transaction data
|
|
245
|
+
root: root,
|
|
246
|
+
inputNullifier: inputNullifiers, // Use resolved values instead of Promise objects
|
|
247
|
+
outputCommitment: outputCommitments, // Use resolved values instead of Promise objects
|
|
248
|
+
publicAmount: publicAmountForCircuit.toString(), // Use proper field arithmetic result
|
|
249
|
+
extDataHash: calculatedExtDataHash,
|
|
250
|
+
// Input UTXO data (UTXOs being spent) - ensure all values are in decimal format
|
|
251
|
+
inAmount: inputs.map(x => x.amount.toString(10)),
|
|
252
|
+
inPrivateKey: inputs.map(x => x.keypair.privkey),
|
|
253
|
+
inBlinding: inputs.map(x => x.blinding.toString(10)),
|
|
254
|
+
inPathIndices: inputMerklePathIndices,
|
|
255
|
+
inPathElements: inputMerklePathElements,
|
|
256
|
+
// Output UTXO data (UTXOs being created) - ensure all values are in decimal format
|
|
257
|
+
outAmount: outputs.map(x => x.amount.toString(10)),
|
|
258
|
+
outBlinding: outputs.map(x => x.blinding.toString(10)),
|
|
259
|
+
outPubkey: outputs.map(x => x.keypair.pubkey),
|
|
260
|
+
// new mint address
|
|
261
|
+
mintAddress: inputs[0].mintAddress
|
|
262
|
+
};
|
|
263
|
+
logger.info('generating ZK proof...');
|
|
264
|
+
// Generate the zero-knowledge proof
|
|
265
|
+
return await prove(input, keyBasePath);
|
|
266
|
+
};
|
|
267
|
+
let getProveResult = null;
|
|
268
|
+
while (!getProveResult) {
|
|
269
|
+
try {
|
|
270
|
+
getProveResult = await getProve();
|
|
155
271
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
firstUtxoMerkleProof.pathElements, // Real Merkle proof for first existing UTXO
|
|
164
|
-
secondUtxo.amount.gt(new BN(0)) ? secondUtxoMerkleProof.pathElements : [...new Array(tree.levels).fill("0")] // Real proof or zero-filled for dummy
|
|
165
|
-
];
|
|
166
|
-
logger.debug(`Using first UTXO with amount: ${firstUtxo.amount.toString()} and index: ${firstUtxo.index}`);
|
|
167
|
-
logger.debug(`Using second ${secondUtxo.amount.gt(new BN(0)) ? 'UTXO' : 'dummy UTXO'} with amount: ${secondUtxo.amount.toString()}${secondUtxo.amount.gt(new BN(0)) ? ` and index: ${secondUtxo.index}` : ''}`);
|
|
168
|
-
logger.debug(`First UTXO Merkle proof path indices from API: [${firstUtxoMerkleProof.pathIndices.join(', ')}]`);
|
|
169
|
-
if (secondUtxo.amount.gt(new BN(0))) {
|
|
170
|
-
logger.debug(`Second UTXO Merkle proof path indices from API: [${secondUtxoMerkleProof.pathIndices.join(', ')}]`);
|
|
272
|
+
catch (error) {
|
|
273
|
+
logger.error('Proof generation failed, retrying...', error);
|
|
274
|
+
getProveRetryCount++;
|
|
275
|
+
if (getProveRetryCount >= 2) {
|
|
276
|
+
throw new Error('Proof generation failed after 2 attempts. Please try again later.');
|
|
277
|
+
}
|
|
278
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
171
279
|
}
|
|
172
280
|
}
|
|
173
|
-
const
|
|
174
|
-
logger.debug(`Public amount calculation: (${extAmount} - ${fee_amount_in_lamports} + FIELD_SIZE) % FIELD_SIZE = ${publicAmountForCircuit.toString()}`);
|
|
175
|
-
// Create outputs for the transaction with the same shared keypair
|
|
176
|
-
const outputs = [
|
|
177
|
-
new Utxo({
|
|
178
|
-
lightWasm,
|
|
179
|
-
amount: outputAmount,
|
|
180
|
-
keypair: utxoKeypair,
|
|
181
|
-
index: currentNextIndex // This UTXO will be inserted at currentNextIndex
|
|
182
|
-
}), // Output with value (either deposit amount minus fee, or input amount minus fee)
|
|
183
|
-
new Utxo({
|
|
184
|
-
lightWasm,
|
|
185
|
-
amount: '0',
|
|
186
|
-
keypair: utxoKeypair,
|
|
187
|
-
index: currentNextIndex + 1 // This UTXO will be inserted at currentNextIndex + 1
|
|
188
|
-
}) // Empty UTXO
|
|
189
|
-
];
|
|
190
|
-
// Verify this matches the circuit balance equation: sumIns + publicAmount = sumOuts
|
|
191
|
-
const sumIns = inputs.reduce((sum, input) => sum.add(input.amount), new BN(0));
|
|
192
|
-
const sumOuts = outputs.reduce((sum, output) => sum.add(output.amount), new BN(0));
|
|
193
|
-
logger.debug(`Circuit balance check: sumIns(${sumIns.toString()}) + publicAmount(${publicAmountForCircuit.toString()}) should equal sumOuts(${sumOuts.toString()})`);
|
|
194
|
-
// Convert to circuit-compatible format
|
|
195
|
-
const publicAmountCircuitResult = sumIns.add(publicAmountForCircuit).mod(FIELD_SIZE);
|
|
196
|
-
logger.debug(`Balance verification: ${sumIns.toString()} + ${publicAmountForCircuit.toString()} (mod FIELD_SIZE) = ${publicAmountCircuitResult.toString()}`);
|
|
197
|
-
logger.debug(`Expected sum of outputs: ${sumOuts.toString()}`);
|
|
198
|
-
logger.debug(`Balance equation satisfied: ${publicAmountCircuitResult.eq(sumOuts)}`);
|
|
199
|
-
// Generate nullifiers and commitments
|
|
200
|
-
const inputNullifiers = await Promise.all(inputs.map(x => x.getNullifier()));
|
|
201
|
-
const outputCommitments = await Promise.all(outputs.map(x => x.getCommitment()));
|
|
202
|
-
// Save original commitment and nullifier values for verification
|
|
203
|
-
logger.debug('\n=== UTXO VALIDATION ===');
|
|
204
|
-
logger.debug('Output 0 Commitment:', outputCommitments[0]);
|
|
205
|
-
logger.debug('Output 1 Commitment:', outputCommitments[1]);
|
|
206
|
-
// Encrypt the UTXO data using a compact format that includes the keypair
|
|
207
|
-
logger.debug('\nEncrypting UTXOs with keypair data...');
|
|
208
|
-
const encryptedOutput1 = encryptionService.encryptUtxo(outputs[0]);
|
|
209
|
-
const encryptedOutput2 = encryptionService.encryptUtxo(outputs[1]);
|
|
210
|
-
logger.debug(`\nOutput[0] (with value):`);
|
|
211
|
-
await outputs[0].log();
|
|
212
|
-
logger.debug(`\nOutput[1] (empty):`);
|
|
213
|
-
await outputs[1].log();
|
|
214
|
-
logger.debug(`\nEncrypted output 1 size: ${encryptedOutput1.length} bytes`);
|
|
215
|
-
logger.debug(`Encrypted output 2 size: ${encryptedOutput2.length} bytes`);
|
|
216
|
-
logger.debug(`Total encrypted outputs size: ${encryptedOutput1.length + encryptedOutput2.length} bytes`);
|
|
217
|
-
// Test decryption to verify commitment values match
|
|
218
|
-
logger.debug('\n=== TESTING DECRYPTION ===');
|
|
219
|
-
logger.debug('Decrypting output 1 to verify commitment matches...');
|
|
220
|
-
const decryptedUtxo1 = await encryptionService.decryptUtxo(encryptedOutput1, lightWasm);
|
|
221
|
-
const decryptedCommitment1 = await decryptedUtxo1.getCommitment();
|
|
222
|
-
logger.debug('Original commitment:', outputCommitments[0]);
|
|
223
|
-
logger.debug('Decrypted commitment:', decryptedCommitment1);
|
|
224
|
-
logger.debug('Commitment matches:', outputCommitments[0] === decryptedCommitment1);
|
|
225
|
-
// Create the deposit ExtData with real encrypted outputs
|
|
226
|
-
const extData = {
|
|
227
|
-
// recipient - just a placeholder, not actually used for deposits.
|
|
228
|
-
recipient: new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM'),
|
|
229
|
-
extAmount: new BN(extAmount),
|
|
230
|
-
encryptedOutput1: encryptedOutput1,
|
|
231
|
-
encryptedOutput2: encryptedOutput2,
|
|
232
|
-
fee: new BN(fee_amount_in_lamports),
|
|
233
|
-
feeRecipient: FEE_RECIPIENT,
|
|
234
|
-
mintAddress: inputs[0].mintAddress
|
|
235
|
-
};
|
|
236
|
-
// Calculate the extDataHash with the encrypted outputs (now includes mintAddress for security)
|
|
237
|
-
const calculatedExtDataHash = getExtDataHash(extData);
|
|
238
|
-
// Create the input for the proof generation (must match circuit input order exactly)
|
|
239
|
-
const input = {
|
|
240
|
-
// Common transaction data
|
|
241
|
-
root: root,
|
|
242
|
-
inputNullifier: inputNullifiers, // Use resolved values instead of Promise objects
|
|
243
|
-
outputCommitment: outputCommitments, // Use resolved values instead of Promise objects
|
|
244
|
-
publicAmount: publicAmountForCircuit.toString(), // Use proper field arithmetic result
|
|
245
|
-
extDataHash: calculatedExtDataHash,
|
|
246
|
-
// Input UTXO data (UTXOs being spent) - ensure all values are in decimal format
|
|
247
|
-
inAmount: inputs.map(x => x.amount.toString(10)),
|
|
248
|
-
inPrivateKey: inputs.map(x => x.keypair.privkey),
|
|
249
|
-
inBlinding: inputs.map(x => x.blinding.toString(10)),
|
|
250
|
-
inPathIndices: inputMerklePathIndices,
|
|
251
|
-
inPathElements: inputMerklePathElements,
|
|
252
|
-
// Output UTXO data (UTXOs being created) - ensure all values are in decimal format
|
|
253
|
-
outAmount: outputs.map(x => x.amount.toString(10)),
|
|
254
|
-
outBlinding: outputs.map(x => x.blinding.toString(10)),
|
|
255
|
-
outPubkey: outputs.map(x => x.keypair.pubkey),
|
|
256
|
-
// new mint address
|
|
257
|
-
mintAddress: inputs[0].mintAddress
|
|
258
|
-
};
|
|
259
|
-
logger.info('generating ZK proof...');
|
|
260
|
-
// Generate the zero-knowledge proof
|
|
261
|
-
const { proof, publicSignals } = await prove(input, keyBasePath);
|
|
281
|
+
const { proof, publicSignals } = getProveResult;
|
|
262
282
|
// Parse the proof and public signals into byte arrays
|
|
263
283
|
const proofInBytes = parseProofToBytesArray(proof);
|
|
264
284
|
const inputsInBytes = parseToBytesArray(publicSignals);
|
package/dist/depositSPL.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Connection, PublicKey, VersionedTransaction } from '@solana/web3.js';
|
|
2
1
|
import * as hasher from '@lightprotocol/hasher.rs';
|
|
2
|
+
import { Connection, PublicKey, VersionedTransaction } from '@solana/web3.js';
|
|
3
3
|
import { EncryptionService } from './utils/encryption.js';
|
|
4
4
|
type DepositParams = {
|
|
5
5
|
mintAddress: PublicKey | string;
|