privacycash 1.1.19 → 1.1.20
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 +21 -17
- package/dist/depositSPL.d.ts +1 -1
- package/dist/depositSPL.js +22 -18
- package/dist/models/utxo.d.ts +2 -2
- package/dist/models/utxo.js +3 -3
- package/dist/utils/utils.d.ts +8 -2
- package/dist/utils/utils.js +10 -10
- package/dist/withdraw.d.ts +1 -1
- package/dist/withdraw.js +23 -22
- package/dist/withdrawSPL.d.ts +1 -1
- package/dist/withdrawSPL.js +9 -24
- package/package.json +1 -1
- package/src/deposit.ts +22 -22
- package/src/depositSPL.ts +26 -22
- package/src/models/utxo.ts +4 -4
- package/src/utils/utils.ts +17 -12
- package/src/withdraw.ts +25 -27
- package/src/withdrawSPL.ts +12 -30
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,7 +84,12 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
88
84
|
let inputs;
|
|
89
85
|
let inputMerklePathIndices;
|
|
90
86
|
let inputMerklePathElements;
|
|
87
|
+
let root;
|
|
88
|
+
let nextIndex;
|
|
91
89
|
if (existingUnspentUtxos.length === 0) {
|
|
90
|
+
const treeState = await queryRemoteTreeState();
|
|
91
|
+
root = treeState.root;
|
|
92
|
+
nextIndex = treeState.nextIndex;
|
|
92
93
|
// Scenario 1: Fresh deposit with dummy inputs - add new funds to the system
|
|
93
94
|
extAmount = amount_in_lamports;
|
|
94
95
|
outputAmount = new BN(amount_in_lamports).sub(new BN(fee_amount_in_lamports)).toString();
|
|
@@ -144,15 +145,18 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
144
145
|
];
|
|
145
146
|
// Fetch Merkle proofs for real UTXOs
|
|
146
147
|
const firstUtxoCommitment = await firstUtxo.getCommitment();
|
|
147
|
-
|
|
148
|
-
let secondUtxoMerkleProof;
|
|
148
|
+
let commitmentsToFetch = [firstUtxoCommitment];
|
|
149
149
|
if (secondUtxo.amount.gt(new BN(0))) {
|
|
150
150
|
// Second UTXO is real, fetch its proof
|
|
151
151
|
const secondUtxoCommitment = await secondUtxo.getCommitment();
|
|
152
|
-
|
|
152
|
+
commitmentsToFetch.push(secondUtxoCommitment);
|
|
153
153
|
logger.debug('\nSecond UTXO to be consolidated:');
|
|
154
154
|
await secondUtxo.log();
|
|
155
155
|
}
|
|
156
|
+
let data = await fetchMerkleProof(commitmentsToFetch);
|
|
157
|
+
root = data.root;
|
|
158
|
+
nextIndex = data.nextIndex;
|
|
159
|
+
let [firstUtxoMerkleProof, secondUtxoMerkleProof] = data.proofs;
|
|
156
160
|
// Use the real pathIndices from API for real inputs, mock index for dummy input
|
|
157
161
|
inputMerklePathIndices = [
|
|
158
162
|
firstUtxo.index || 0, // Use the real UTXO's index
|
|
@@ -178,13 +182,13 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
178
182
|
lightWasm,
|
|
179
183
|
amount: outputAmount,
|
|
180
184
|
keypair: utxoKeypair,
|
|
181
|
-
index:
|
|
185
|
+
index: nextIndex // This UTXO will be inserted at currentNextIndex
|
|
182
186
|
}), // Output with value (either deposit amount minus fee, or input amount minus fee)
|
|
183
187
|
new Utxo({
|
|
184
188
|
lightWasm,
|
|
185
189
|
amount: '0',
|
|
186
190
|
keypair: utxoKeypair,
|
|
187
|
-
index:
|
|
191
|
+
index: nextIndex + 1 // This UTXO will be inserted at currentNextIndex + 1
|
|
188
192
|
}) // Empty UTXO
|
|
189
193
|
];
|
|
190
194
|
// Verify this matches the circuit balance equation: sumIns + publicAmount = sumOuts
|
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;
|
package/dist/depositSPL.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAccount, getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
2
|
+
import { ComputeBudgetProgram, PublicKey, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js';
|
|
2
3
|
import BN from 'bn.js';
|
|
3
|
-
import { Utxo } from './models/utxo.js';
|
|
4
|
-
import { fetchMerkleProof, findNullifierPDAs, getProgramAccounts, queryRemoteTreeState, findCrossCheckNullifierPDAs, getExtDataHash, getMintAddressField } 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
4
|
import { getUtxosSPL } from './getUtxosSPL.js';
|
|
10
|
-
import {
|
|
5
|
+
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
6
|
+
import { Utxo } from './models/utxo.js';
|
|
11
7
|
import { useExistingALT } from './utils/address_lookup_table.js';
|
|
8
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, MERKLE_TREE_DEPTH, PROGRAM_ID, RELAYER_API_URL, tokens } from './utils/constants.js';
|
|
9
|
+
import { serializeProofAndExtData } from './utils/encryption.js';
|
|
12
10
|
import { logger } from './utils/logger.js';
|
|
13
|
-
import {
|
|
11
|
+
import { MerkleTree } from './utils/merkle_tree.js';
|
|
12
|
+
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
13
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getMintAddressField, getProgramAccounts, queryRemoteTreeState } from './utils/utils.js';
|
|
14
14
|
// Function to relay pre-signed deposit transaction to indexer backend
|
|
15
15
|
async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, mintAddress }) {
|
|
16
16
|
try {
|
|
@@ -106,10 +106,6 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
106
106
|
const { globalConfigAccount } = getProgramAccounts();
|
|
107
107
|
// Create the merkle tree with the pre-initialized poseidon hash
|
|
108
108
|
const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
|
|
109
|
-
// Initialize root and nextIndex variables
|
|
110
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
|
|
111
|
-
logger.debug(`Using tree root: ${root}`);
|
|
112
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
113
109
|
// Generate a deterministic private key derived from the wallet keypair
|
|
114
110
|
// const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
115
111
|
const utxoPrivateKey = encryptionService.getUtxoPrivateKeyV2();
|
|
@@ -126,7 +122,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
126
122
|
let inputs;
|
|
127
123
|
let inputMerklePathIndices;
|
|
128
124
|
let inputMerklePathElements;
|
|
125
|
+
let root;
|
|
126
|
+
let nextIndex;
|
|
129
127
|
if (mintUtxos.length === 0) {
|
|
128
|
+
const treeState = await queryRemoteTreeState(token.name);
|
|
129
|
+
root = treeState.root;
|
|
130
|
+
nextIndex = treeState.nextIndex;
|
|
130
131
|
// Scenario 1: Fresh deposit with dummy inputs - add new funds to the system
|
|
131
132
|
extAmount = base_units;
|
|
132
133
|
outputAmount = new BN(base_units).sub(new BN(fee_base_units)).toString();
|
|
@@ -184,15 +185,18 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
184
185
|
];
|
|
185
186
|
// Fetch Merkle proofs for real UTXOs
|
|
186
187
|
const firstUtxoCommitment = await firstUtxo.getCommitment();
|
|
187
|
-
|
|
188
|
-
let secondUtxoMerkleProof;
|
|
188
|
+
let commitmentsToFetch = [firstUtxoCommitment];
|
|
189
189
|
if (secondUtxo.amount.gt(new BN(0))) {
|
|
190
190
|
// Second UTXO is real, fetch its proof
|
|
191
191
|
const secondUtxoCommitment = await secondUtxo.getCommitment();
|
|
192
|
-
|
|
192
|
+
commitmentsToFetch.push(secondUtxoCommitment);
|
|
193
193
|
logger.debug('\nSecond UTXO to be consolidated:');
|
|
194
194
|
await secondUtxo.log();
|
|
195
195
|
}
|
|
196
|
+
let data = await fetchMerkleProof(commitmentsToFetch, token.name);
|
|
197
|
+
root = data.root;
|
|
198
|
+
nextIndex = data.nextIndex;
|
|
199
|
+
let [firstUtxoMerkleProof, secondUtxoMerkleProof] = data.proofs;
|
|
196
200
|
// Use the real pathIndices from API for real inputs, mock index for dummy input
|
|
197
201
|
inputMerklePathIndices = [
|
|
198
202
|
firstUtxo.index || 0, // Use the real UTXO's index
|
|
@@ -218,14 +222,14 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
218
222
|
lightWasm,
|
|
219
223
|
amount: outputAmount,
|
|
220
224
|
keypair: utxoKeypair,
|
|
221
|
-
index:
|
|
225
|
+
index: nextIndex, // This UTXO will be inserted at currentNextIndex
|
|
222
226
|
mintAddress: token.pubkey.toString()
|
|
223
227
|
}), // Output with value (either deposit amount minus fee, or input amount minus fee)
|
|
224
228
|
new Utxo({
|
|
225
229
|
lightWasm,
|
|
226
230
|
amount: '0',
|
|
227
231
|
keypair: utxoKeypair,
|
|
228
|
-
index:
|
|
232
|
+
index: nextIndex + 1, // This UTXO will be inserted at currentNextIndex
|
|
229
233
|
mintAddress: token.pubkey.toString()
|
|
230
234
|
}) // Empty UTXO
|
|
231
235
|
];
|
package/dist/models/utxo.d.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Provides UTXO functionality for the ZK Cash system
|
|
5
5
|
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
6
|
*/
|
|
7
|
+
import * as hasher from '@lightprotocol/hasher.rs';
|
|
7
8
|
import BN from 'bn.js';
|
|
8
9
|
import { Keypair } from './keypair.js';
|
|
9
|
-
import * as hasher from '@lightprotocol/hasher.rs';
|
|
10
10
|
/**
|
|
11
11
|
* Simplified Utxo class inspired by Tornado Cash Nova
|
|
12
12
|
* Based on: https://github.com/tornadocash/tornado-nova/blob/f9264eeffe48bf5e04e19d8086ee6ec58cdf0d9e/src/utxo.js
|
|
@@ -39,7 +39,7 @@ export declare class Utxo {
|
|
|
39
39
|
mintAddress?: string;
|
|
40
40
|
version?: 'v1' | 'v2';
|
|
41
41
|
});
|
|
42
|
-
getCommitment():
|
|
42
|
+
getCommitment(): string;
|
|
43
43
|
getNullifier(): Promise<string>;
|
|
44
44
|
/**
|
|
45
45
|
* Log all the UTXO's public properties and derived values in JSON format
|
package/dist/models/utxo.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
* Provides UTXO functionality for the ZK Cash system
|
|
5
5
|
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
6
|
*/
|
|
7
|
+
import { PublicKey } from '@solana/web3.js';
|
|
7
8
|
import BN from 'bn.js';
|
|
8
|
-
import { Keypair } from './keypair.js';
|
|
9
9
|
import { ethers } from 'ethers';
|
|
10
10
|
import { getMintAddressField } from '../utils/utils.js';
|
|
11
|
-
import {
|
|
11
|
+
import { Keypair } from './keypair.js';
|
|
12
12
|
/**
|
|
13
13
|
* Simplified Utxo class inspired by Tornado Cash Nova
|
|
14
14
|
* Based on: https://github.com/tornadocash/tornado-nova/blob/f9264eeffe48bf5e04e19d8086ee6ec58cdf0d9e/src/utxo.js
|
|
@@ -41,7 +41,7 @@ export class Utxo {
|
|
|
41
41
|
this.mintAddress = mintAddress;
|
|
42
42
|
this.version = version;
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
getCommitment() {
|
|
45
45
|
// return this.lightWasm.poseidonHashString([this.amount.toString(), this.keypair.pubkey.toString(), this.blinding.toString(), this.mintAddress]);
|
|
46
46
|
const mintAddressField = getMintAddressField(new PublicKey(this.mintAddress));
|
|
47
47
|
return this.lightWasm.poseidonHashString([
|
package/dist/utils/utils.d.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Provides common utility functions for the ZK Cash system
|
|
5
5
|
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
6
|
*/
|
|
7
|
+
import { PublicKey } from '@solana/web3.js';
|
|
7
8
|
import BN from 'bn.js';
|
|
8
9
|
import { Utxo } from '../models/utxo.js';
|
|
9
|
-
import { PublicKey } from '@solana/web3.js';
|
|
10
10
|
/**
|
|
11
11
|
* Calculate deposit fee based on deposit amount and fee rate
|
|
12
12
|
* @param depositAmount Amount being deposited in lamports
|
|
@@ -40,9 +40,14 @@ export declare function getExtDataHash(extData: {
|
|
|
40
40
|
feeRecipient: string | PublicKey;
|
|
41
41
|
mintAddress: string | PublicKey;
|
|
42
42
|
}): Uint8Array;
|
|
43
|
-
|
|
43
|
+
type Proof = {
|
|
44
44
|
pathElements: string[];
|
|
45
45
|
pathIndices: number[];
|
|
46
|
+
};
|
|
47
|
+
export declare function fetchMerkleProof(commitments: string[], tokenName?: string): Promise<{
|
|
48
|
+
proofs: Proof[];
|
|
49
|
+
root: string;
|
|
50
|
+
nextIndex: number;
|
|
46
51
|
}>;
|
|
47
52
|
export declare function findNullifierPDAs(proof: any): {
|
|
48
53
|
nullifier0PDA: PublicKey;
|
|
@@ -62,3 +67,4 @@ export declare function findCrossCheckNullifierPDAs(proof: any): {
|
|
|
62
67
|
nullifier3PDA: PublicKey;
|
|
63
68
|
};
|
|
64
69
|
export declare function getMintAddressField(mint: PublicKey): string;
|
|
70
|
+
export {};
|
package/dist/utils/utils.js
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
* Provides common utility functions for the ZK Cash system
|
|
5
5
|
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
6
|
*/
|
|
7
|
-
import BN from 'bn.js';
|
|
8
|
-
import * as borsh from 'borsh';
|
|
9
7
|
import { sha256 } from '@ethersproject/sha2';
|
|
10
8
|
import { PublicKey } from '@solana/web3.js';
|
|
11
|
-
import
|
|
12
|
-
import
|
|
9
|
+
import BN from 'bn.js';
|
|
10
|
+
import * as borsh from 'borsh';
|
|
13
11
|
import { getConfig } from '../config.js';
|
|
12
|
+
import { PROGRAM_ID, RELAYER_API_URL } from './constants.js';
|
|
13
|
+
import { logger } from './logger.js';
|
|
14
14
|
/**
|
|
15
15
|
* Calculate deposit fee based on deposit amount and fee rate
|
|
16
16
|
* @param depositAmount Amount being deposited in lamports
|
|
@@ -91,23 +91,23 @@ export function getExtDataHash(extData) {
|
|
|
91
91
|
return Buffer.from(hashHex.slice(2), 'hex');
|
|
92
92
|
}
|
|
93
93
|
// Function to fetch Merkle proof from API for a given commitment
|
|
94
|
-
export async function fetchMerkleProof(
|
|
94
|
+
export async function fetchMerkleProof(commitments, tokenName) {
|
|
95
95
|
try {
|
|
96
|
-
logger.debug(`Fetching Merkle proof for commitment: ${
|
|
97
|
-
let url = `${RELAYER_API_URL}/merkle/
|
|
96
|
+
logger.debug(`Fetching Merkle proof for commitment: ${commitments.join(', ')} with token: ${tokenName || 'N/A'}`);
|
|
97
|
+
let url = `${RELAYER_API_URL}/merkle/proofv2/?commitments=${commitments.join(',')}`;
|
|
98
98
|
if (tokenName) {
|
|
99
|
-
url += '
|
|
99
|
+
url += '&token=' + tokenName;
|
|
100
100
|
}
|
|
101
|
+
logger.debug(`Fetching Merkle proof from URL: ${url}`);
|
|
101
102
|
const response = await fetch(url);
|
|
102
103
|
if (!response.ok) {
|
|
103
104
|
throw new Error(`Failed to fetch Merkle proof: ${url}`);
|
|
104
105
|
}
|
|
105
106
|
const data = await response.json();
|
|
106
|
-
logger.debug(`✓ Fetched Merkle proof with ${data.pathElements.length} elements`);
|
|
107
107
|
return data;
|
|
108
108
|
}
|
|
109
109
|
catch (error) {
|
|
110
|
-
console.error(`Failed to fetch Merkle proof for
|
|
110
|
+
console.error(`Failed to fetch Merkle proof for commitments ${commitments.join(', ')}:`, error);
|
|
111
111
|
throw error;
|
|
112
112
|
}
|
|
113
113
|
}
|
package/dist/withdraw.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
1
|
import * as hasher from '@lightprotocol/hasher.rs';
|
|
2
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { EncryptionService } from './utils/encryption.js';
|
|
4
4
|
type WithdrawParams = {
|
|
5
5
|
publicKey: PublicKey;
|
package/dist/withdraw.js
CHANGED
|
@@ -4,12 +4,12 @@ import { Buffer } from 'buffer';
|
|
|
4
4
|
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
5
5
|
import { Utxo } from './models/utxo.js';
|
|
6
6
|
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
7
|
-
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, RELAYER_API_URL
|
|
7
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, RELAYER_API_URL } from './utils/constants.js';
|
|
8
8
|
import { serializeProofAndExtData } from './utils/encryption.js';
|
|
9
|
-
import { fetchMerkleProof, findNullifierPDAs, getExtDataHash, getProgramAccounts
|
|
9
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getProgramAccounts } from './utils/utils.js';
|
|
10
|
+
import { getConfig } from './config.js';
|
|
10
11
|
import { getUtxos } from './getUtxos.js';
|
|
11
12
|
import { logger } from './utils/logger.js';
|
|
12
|
-
import { getConfig } from './config.js';
|
|
13
13
|
// Indexer API endpoint
|
|
14
14
|
// Function to submit withdraw request to indexer backend
|
|
15
15
|
async function submitWithdrawToIndexer(params) {
|
|
@@ -41,10 +41,6 @@ export async function withdraw({ recipient, lightWasm, storage, publicKey, conne
|
|
|
41
41
|
let isPartial = false;
|
|
42
42
|
logger.debug('Encryption key generated from user keypair');
|
|
43
43
|
const { treeAccount, treeTokenAccount, globalConfigAccount } = getProgramAccounts();
|
|
44
|
-
// Get current tree state
|
|
45
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState();
|
|
46
|
-
logger.debug(`Using tree root: ${root}`);
|
|
47
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
48
44
|
// Generate a deterministic private key derived from the wallet keypair
|
|
49
45
|
const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
50
46
|
// Create a UTXO keypair that will be used for all inputs and outputs
|
|
@@ -89,20 +85,25 @@ export async function withdraw({ recipient, lightWasm, storage, publicKey, conne
|
|
|
89
85
|
const changeAmount = totalInputAmount.sub(new BN(amount_in_lamports)).sub(new BN(fee_in_lamports));
|
|
90
86
|
logger.debug(`Withdrawing ${amount_in_lamports} lamports with ${fee_in_lamports} fee, ${changeAmount.toString()} as change`);
|
|
91
87
|
// Get Merkle proofs for both input UTXOs
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
88
|
+
let commitmentsToFetch = inputs.map(utxo => utxo.getCommitment());
|
|
89
|
+
console.log('commitmentsToFetch', commitmentsToFetch);
|
|
90
|
+
const { root, nextIndex, proofs } = await fetchMerkleProof(commitmentsToFetch);
|
|
91
|
+
// const inputMerkleProofs = await Promise.all(
|
|
92
|
+
// inputs.map(async (utxo, index) => {
|
|
93
|
+
// // For dummy UTXO (amount is 0), use a zero-filled proof
|
|
94
|
+
// if (utxo.amount.eq(new BN(0))) {
|
|
95
|
+
// return {
|
|
96
|
+
// pathElements: [...new Array(MERKLE_TREE_DEPTH).fill("0")],
|
|
97
|
+
// pathIndices: Array(MERKLE_TREE_DEPTH).fill(0)
|
|
98
|
+
// };
|
|
99
|
+
// }
|
|
100
|
+
// // For real UTXOs, fetch the proof from API
|
|
101
|
+
// const commitment = await utxo.getCommitment();
|
|
102
|
+
// return fetchMerkleProof(commitment);
|
|
103
|
+
// })
|
|
104
|
+
// );
|
|
104
105
|
// Extract path elements and indices
|
|
105
|
-
const inputMerklePathElements =
|
|
106
|
+
const inputMerklePathElements = proofs.map(proof => proof.pathElements);
|
|
106
107
|
const inputMerklePathIndices = inputs.map(utxo => utxo.index || 0);
|
|
107
108
|
// Create outputs: first output is change, second is dummy (required by protocol)
|
|
108
109
|
const outputs = [
|
|
@@ -110,13 +111,13 @@ export async function withdraw({ recipient, lightWasm, storage, publicKey, conne
|
|
|
110
111
|
lightWasm,
|
|
111
112
|
amount: changeAmount.toString(),
|
|
112
113
|
keypair: utxoKeypairV2,
|
|
113
|
-
index:
|
|
114
|
+
index: nextIndex
|
|
114
115
|
}), // Change output
|
|
115
116
|
new Utxo({
|
|
116
117
|
lightWasm,
|
|
117
118
|
amount: '0',
|
|
118
119
|
keypair: utxoKeypairV2,
|
|
119
|
-
index:
|
|
120
|
+
index: nextIndex + 1
|
|
120
121
|
}) // Empty UTXO
|
|
121
122
|
];
|
|
122
123
|
// For withdrawals, extAmount is negative (funds leaving the system)
|
package/dist/withdrawSPL.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
1
|
import * as hasher from '@lightprotocol/hasher.rs';
|
|
2
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { EncryptionService } from './utils/encryption.js';
|
|
4
4
|
type WithdrawParams = {
|
|
5
5
|
publicKey: PublicKey;
|
package/dist/withdrawSPL.js
CHANGED
|
@@ -4,13 +4,13 @@ import { Buffer } from 'buffer';
|
|
|
4
4
|
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
5
5
|
import { Utxo } from './models/utxo.js';
|
|
6
6
|
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
7
|
-
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE,
|
|
7
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, PROGRAM_ID, RELAYER_API_URL, tokens } from './utils/constants.js';
|
|
8
8
|
import { serializeProofAndExtData } from './utils/encryption.js';
|
|
9
|
-
import { fetchMerkleProof,
|
|
9
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getMintAddressField, getProgramAccounts } from './utils/utils.js';
|
|
10
|
+
import { getAssociatedTokenAddressSync, getMint } from '@solana/spl-token';
|
|
11
|
+
import { getConfig } from './config.js';
|
|
10
12
|
import { getUtxosSPL } from './getUtxosSPL.js';
|
|
11
13
|
import { logger } from './utils/logger.js';
|
|
12
|
-
import { getConfig } from './config.js';
|
|
13
|
-
import { getAssociatedTokenAddressSync, getMint } from '@solana/spl-token';
|
|
14
14
|
// Indexer API endpoint
|
|
15
15
|
// Function to submit withdraw request to indexer backend
|
|
16
16
|
async function submitWithdrawToIndexer(params) {
|
|
@@ -71,10 +71,6 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
71
71
|
// Derive tree account PDA with mint address for SPL (different from SOL version)
|
|
72
72
|
const [treeAccount] = PublicKey.findProgramAddressSync([Buffer.from('merkle_tree'), token.pubkey.toBuffer()], PROGRAM_ID);
|
|
73
73
|
const { globalConfigAccount, treeTokenAccount } = getProgramAccounts();
|
|
74
|
-
// Get current tree state
|
|
75
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
|
|
76
|
-
logger.debug(`Using tree root: ${root}`);
|
|
77
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
78
74
|
// Generate a deterministic private key derived from the wallet keypair
|
|
79
75
|
const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
80
76
|
// Create a UTXO keypair that will be used for all inputs and outputs
|
|
@@ -119,21 +115,10 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
119
115
|
// Calculate the change amount (what's left after withdrawal and fee)
|
|
120
116
|
const changeAmount = totalInputAmount.sub(new BN(base_units)).sub(new BN(fee_base_units));
|
|
121
117
|
logger.debug(`Withdrawing ${base_units} lamports with ${fee_base_units} fee, ${changeAmount.toString()} as change`);
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
// For dummy UTXO (amount is 0), use a zero-filled proof
|
|
125
|
-
if (utxo.amount.eq(new BN(0))) {
|
|
126
|
-
return {
|
|
127
|
-
pathElements: [...new Array(MERKLE_TREE_DEPTH).fill("0")],
|
|
128
|
-
pathIndices: Array(MERKLE_TREE_DEPTH).fill(0)
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
// For real UTXOs, fetch the proof from API
|
|
132
|
-
const commitment = await utxo.getCommitment();
|
|
133
|
-
return fetchMerkleProof(commitment, token.name);
|
|
134
|
-
}));
|
|
118
|
+
let commitmentsToFetch = inputs.map(utxo => utxo.getCommitment());
|
|
119
|
+
const { root, nextIndex, proofs } = await fetchMerkleProof(commitmentsToFetch, token.name);
|
|
135
120
|
// Extract path elements and indices
|
|
136
|
-
const inputMerklePathElements =
|
|
121
|
+
const inputMerklePathElements = proofs.map(proof => proof.pathElements);
|
|
137
122
|
const inputMerklePathIndices = inputs.map(utxo => utxo.index || 0);
|
|
138
123
|
// Create outputs: first output is change, second is dummy (required by protocol)
|
|
139
124
|
const outputs = [
|
|
@@ -141,14 +126,14 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
141
126
|
lightWasm,
|
|
142
127
|
amount: changeAmount.toString(),
|
|
143
128
|
keypair: utxoKeypairV2,
|
|
144
|
-
index:
|
|
129
|
+
index: nextIndex,
|
|
145
130
|
mintAddress: token.pubkey.toString()
|
|
146
131
|
}), // Change output
|
|
147
132
|
new Utxo({
|
|
148
133
|
lightWasm,
|
|
149
134
|
amount: '0',
|
|
150
135
|
keypair: utxoKeypairV2,
|
|
151
|
-
index:
|
|
136
|
+
index: nextIndex + 1,
|
|
152
137
|
mintAddress: token.pubkey.toString()
|
|
153
138
|
}) // Empty UTXO
|
|
154
139
|
];
|
package/package.json
CHANGED
package/src/deposit.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { Connection, Keypair, PublicKey, TransactionInstruction, SystemProgram, ComputeBudgetProgram, VersionedTransaction, TransactionMessage, LAMPORTS_PER_SOL } from '@solana/web3.js';
|
|
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
1
|
import * as hasher from '@lightprotocol/hasher.rs';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
2
|
+
import { ComputeBudgetProgram, Connection, LAMPORTS_PER_SOL, PublicKey, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js';
|
|
3
|
+
import BN from 'bn.js';
|
|
4
|
+
import { getUtxos } from './getUtxos.js';
|
|
9
5
|
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
10
|
-
import {
|
|
11
|
-
import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS } from './utils/constants.js';
|
|
6
|
+
import { Utxo } from './models/utxo.js';
|
|
12
7
|
import { useExistingALT } from './utils/address_lookup_table.js';
|
|
8
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, MERKLE_TREE_DEPTH, PROGRAM_ID, RELAYER_API_URL } from './utils/constants.js';
|
|
9
|
+
import { EncryptionService, serializeProofAndExtData } from './utils/encryption.js';
|
|
13
10
|
import { logger } from './utils/logger.js';
|
|
11
|
+
import { MerkleTree } from './utils/merkle_tree.js';
|
|
12
|
+
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
13
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getProgramAccounts, queryRemoteTreeState } from './utils/utils.js';
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
// Function to relay pre-signed deposit transaction to indexer backend
|
|
@@ -97,12 +97,6 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
97
97
|
// Create the merkle tree with the pre-initialized poseidon hash
|
|
98
98
|
const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
|
|
99
99
|
|
|
100
|
-
// Initialize root and nextIndex variables
|
|
101
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState();
|
|
102
|
-
|
|
103
|
-
logger.debug(`Using tree root: ${root}`);
|
|
104
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
105
|
-
|
|
106
100
|
// Generate a deterministic private key derived from the wallet keypair
|
|
107
101
|
// const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
108
102
|
const utxoPrivateKey = encryptionService.getUtxoPrivateKeyV2();
|
|
@@ -123,8 +117,12 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
123
117
|
let inputs: Utxo[];
|
|
124
118
|
let inputMerklePathIndices: number[];
|
|
125
119
|
let inputMerklePathElements: string[][];
|
|
126
|
-
|
|
120
|
+
let root: string;
|
|
121
|
+
let nextIndex: number;
|
|
127
122
|
if (existingUnspentUtxos.length === 0) {
|
|
123
|
+
const treeState = await queryRemoteTreeState();
|
|
124
|
+
root = treeState.root;
|
|
125
|
+
nextIndex = treeState.nextIndex;
|
|
128
126
|
// Scenario 1: Fresh deposit with dummy inputs - add new funds to the system
|
|
129
127
|
extAmount = amount_in_lamports;
|
|
130
128
|
outputAmount = new BN(amount_in_lamports).sub(new BN(fee_amount_in_lamports)).toString();
|
|
@@ -188,16 +186,18 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
188
186
|
|
|
189
187
|
// Fetch Merkle proofs for real UTXOs
|
|
190
188
|
const firstUtxoCommitment = await firstUtxo.getCommitment();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let secondUtxoMerkleProof;
|
|
189
|
+
let commitmentsToFetch = [firstUtxoCommitment];
|
|
194
190
|
if (secondUtxo.amount.gt(new BN(0))) {
|
|
195
191
|
// Second UTXO is real, fetch its proof
|
|
196
192
|
const secondUtxoCommitment = await secondUtxo.getCommitment();
|
|
197
|
-
|
|
193
|
+
commitmentsToFetch.push(secondUtxoCommitment);
|
|
198
194
|
logger.debug('\nSecond UTXO to be consolidated:');
|
|
199
195
|
await secondUtxo.log();
|
|
200
196
|
}
|
|
197
|
+
let data = await fetchMerkleProof(commitmentsToFetch)
|
|
198
|
+
root = data.root
|
|
199
|
+
nextIndex = data.nextIndex
|
|
200
|
+
let [firstUtxoMerkleProof, secondUtxoMerkleProof] = data.proofs
|
|
201
201
|
|
|
202
202
|
// Use the real pathIndices from API for real inputs, mock index for dummy input
|
|
203
203
|
inputMerklePathIndices = [
|
|
@@ -228,13 +228,13 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
|
|
|
228
228
|
lightWasm,
|
|
229
229
|
amount: outputAmount,
|
|
230
230
|
keypair: utxoKeypair,
|
|
231
|
-
index:
|
|
231
|
+
index: nextIndex // This UTXO will be inserted at currentNextIndex
|
|
232
232
|
}), // Output with value (either deposit amount minus fee, or input amount minus fee)
|
|
233
233
|
new Utxo({
|
|
234
234
|
lightWasm,
|
|
235
235
|
amount: '0',
|
|
236
236
|
keypair: utxoKeypair,
|
|
237
|
-
index:
|
|
237
|
+
index: nextIndex + 1 // This UTXO will be inserted at currentNextIndex + 1
|
|
238
238
|
}) // Empty UTXO
|
|
239
239
|
];
|
|
240
240
|
|
package/src/depositSPL.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as hasher from '@lightprotocol/hasher.rs';
|
|
2
|
+
import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAccount, getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
3
|
+
import { ComputeBudgetProgram, Connection, PublicKey, SystemProgram, TransactionInstruction, TransactionMessage, VersionedTransaction } from '@solana/web3.js';
|
|
2
4
|
import BN from 'bn.js';
|
|
5
|
+
import { getUtxosSPL } from './getUtxosSPL.js';
|
|
6
|
+
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
3
7
|
import { Utxo } from './models/utxo.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import * as hasher from '@lightprotocol/hasher.rs';
|
|
7
|
-
import { MerkleTree } from './utils/merkle_tree.js';
|
|
8
|
+
import { useExistingALT } from './utils/address_lookup_table.js';
|
|
9
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, MERKLE_TREE_DEPTH, PROGRAM_ID, RELAYER_API_URL, Token, tokens } from './utils/constants.js';
|
|
8
10
|
import { EncryptionService, serializeProofAndExtData } from './utils/encryption.js';
|
|
9
|
-
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
10
|
-
import { getUtxosSPL, isUtxoSpent } from './getUtxosSPL.js';
|
|
11
|
-
import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS, tokens, SplList, Token } from './utils/constants.js';
|
|
12
|
-
import { getProtocolAddressesWithMint, useExistingALT } from './utils/address_lookup_table.js';
|
|
13
11
|
import { logger } from './utils/logger.js';
|
|
14
|
-
import {
|
|
12
|
+
import { MerkleTree } from './utils/merkle_tree.js';
|
|
13
|
+
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
14
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getMintAddressField, getProgramAccounts, queryRemoteTreeState } from './utils/utils.js';
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
// Function to relay pre-signed deposit transaction to indexer backend
|
|
@@ -162,12 +162,6 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
162
162
|
// Create the merkle tree with the pre-initialized poseidon hash
|
|
163
163
|
const tree = new MerkleTree(MERKLE_TREE_DEPTH, lightWasm);
|
|
164
164
|
|
|
165
|
-
// Initialize root and nextIndex variables
|
|
166
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
|
|
167
|
-
|
|
168
|
-
logger.debug(`Using tree root: ${root}`);
|
|
169
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
170
|
-
|
|
171
165
|
// Generate a deterministic private key derived from the wallet keypair
|
|
172
166
|
// const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
173
167
|
const utxoPrivateKey = encryptionService.getUtxoPrivateKeyV2();
|
|
@@ -188,8 +182,12 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
188
182
|
let inputs: Utxo[];
|
|
189
183
|
let inputMerklePathIndices: number[];
|
|
190
184
|
let inputMerklePathElements: string[][];
|
|
191
|
-
|
|
185
|
+
let root: string;
|
|
186
|
+
let nextIndex: number;
|
|
192
187
|
if (mintUtxos.length === 0) {
|
|
188
|
+
const treeState = await queryRemoteTreeState(token.name);
|
|
189
|
+
root = treeState.root
|
|
190
|
+
nextIndex = treeState.nextIndex
|
|
193
191
|
// Scenario 1: Fresh deposit with dummy inputs - add new funds to the system
|
|
194
192
|
extAmount = base_units;
|
|
195
193
|
outputAmount = new BN(base_units).sub(new BN(fee_base_units)).toString();
|
|
@@ -241,6 +239,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
241
239
|
logger.debug('\nFirst UTXO to be consolidated:');
|
|
242
240
|
|
|
243
241
|
// Use first existing UTXO as first input, and either second UTXO or dummy UTXO as second input
|
|
242
|
+
|
|
244
243
|
const secondUtxo = mintUtxos.length > 1 ? mintUtxos[1] : new Utxo({
|
|
245
244
|
lightWasm,
|
|
246
245
|
keypair: utxoKeypair,
|
|
@@ -255,17 +254,22 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
255
254
|
|
|
256
255
|
// Fetch Merkle proofs for real UTXOs
|
|
257
256
|
const firstUtxoCommitment = await firstUtxo.getCommitment();
|
|
258
|
-
const firstUtxoMerkleProof = await fetchMerkleProof(firstUtxoCommitment, token.name);
|
|
259
257
|
|
|
260
|
-
let
|
|
258
|
+
let commitmentsToFetch = [firstUtxoCommitment];
|
|
261
259
|
if (secondUtxo.amount.gt(new BN(0))) {
|
|
262
260
|
// Second UTXO is real, fetch its proof
|
|
263
261
|
const secondUtxoCommitment = await secondUtxo.getCommitment();
|
|
264
|
-
|
|
262
|
+
commitmentsToFetch.push(secondUtxoCommitment);
|
|
265
263
|
logger.debug('\nSecond UTXO to be consolidated:');
|
|
266
264
|
await secondUtxo.log();
|
|
267
265
|
}
|
|
268
266
|
|
|
267
|
+
let data = await fetchMerkleProof(commitmentsToFetch, token.name)
|
|
268
|
+
root = data.root
|
|
269
|
+
nextIndex = data.nextIndex
|
|
270
|
+
let [firstUtxoMerkleProof, secondUtxoMerkleProof] = data.proofs
|
|
271
|
+
|
|
272
|
+
|
|
269
273
|
// Use the real pathIndices from API for real inputs, mock index for dummy input
|
|
270
274
|
inputMerklePathIndices = [
|
|
271
275
|
firstUtxo.index || 0, // Use the real UTXO's index
|
|
@@ -295,14 +299,14 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
|
|
|
295
299
|
lightWasm,
|
|
296
300
|
amount: outputAmount,
|
|
297
301
|
keypair: utxoKeypair,
|
|
298
|
-
index:
|
|
302
|
+
index: nextIndex, // This UTXO will be inserted at currentNextIndex
|
|
299
303
|
mintAddress: token.pubkey.toString()
|
|
300
304
|
}), // Output with value (either deposit amount minus fee, or input amount minus fee)
|
|
301
305
|
new Utxo({
|
|
302
306
|
lightWasm,
|
|
303
307
|
amount: '0',
|
|
304
308
|
keypair: utxoKeypair,
|
|
305
|
-
index:
|
|
309
|
+
index: nextIndex + 1, // This UTXO will be inserted at currentNextIndex
|
|
306
310
|
mintAddress: token.pubkey.toString()
|
|
307
311
|
}) // Empty UTXO
|
|
308
312
|
];
|
package/src/models/utxo.ts
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import BN from 'bn.js';
|
|
9
|
-
import { Keypair } from './keypair.js';
|
|
10
8
|
import * as hasher from '@lightprotocol/hasher.rs';
|
|
9
|
+
import { PublicKey } from '@solana/web3.js';
|
|
10
|
+
import BN from 'bn.js';
|
|
11
11
|
import { ethers } from 'ethers';
|
|
12
12
|
import { getMintAddressField } from '../utils/utils.js';
|
|
13
|
-
import {
|
|
13
|
+
import { Keypair } from './keypair.js';
|
|
14
14
|
/**
|
|
15
15
|
* Simplified Utxo class inspired by Tornado Cash Nova
|
|
16
16
|
* Based on: https://github.com/tornadocash/tornado-nova/blob/f9264eeffe48bf5e04e19d8086ee6ec58cdf0d9e/src/utxo.js
|
|
@@ -58,7 +58,7 @@ export class Utxo {
|
|
|
58
58
|
this.version = version;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
getCommitment(): string {
|
|
62
62
|
// return this.lightWasm.poseidonHashString([this.amount.toString(), this.keypair.pubkey.toString(), this.blinding.toString(), this.mintAddress]);
|
|
63
63
|
const mintAddressField = getMintAddressField(new PublicKey(this.mintAddress));
|
|
64
64
|
return this.lightWasm.poseidonHashString([
|
package/src/utils/utils.ts
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* Based on: https://github.com/tornadocash/tornado-nova
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import BN from 'bn.js';
|
|
9
|
-
import { Utxo } from '../models/utxo.js';
|
|
10
|
-
import * as borsh from 'borsh';
|
|
11
8
|
import { sha256 } from '@ethersproject/sha2';
|
|
12
9
|
import { PublicKey } from '@solana/web3.js';
|
|
13
|
-
import
|
|
14
|
-
import
|
|
10
|
+
import BN from 'bn.js';
|
|
11
|
+
import * as borsh from 'borsh';
|
|
15
12
|
import { getConfig } from '../config.js';
|
|
13
|
+
import { Utxo } from '../models/utxo.js';
|
|
14
|
+
import { PROGRAM_ID, RELAYER_API_URL } from './constants.js';
|
|
15
|
+
import { logger } from './logger.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Calculate deposit fee based on deposit amount and fee rate
|
|
@@ -113,24 +113,29 @@ export function getExtDataHash(extData: {
|
|
|
113
113
|
return Buffer.from(hashHex.slice(2), 'hex');
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
type Proof = { pathElements: string[], pathIndices: number[] }
|
|
116
117
|
|
|
117
118
|
// Function to fetch Merkle proof from API for a given commitment
|
|
118
|
-
export async function fetchMerkleProof(
|
|
119
|
+
export async function fetchMerkleProof(commitments: string[], tokenName?: string): Promise<{
|
|
120
|
+
proofs: Proof[],
|
|
121
|
+
root: string,
|
|
122
|
+
nextIndex: number
|
|
123
|
+
}> {
|
|
119
124
|
try {
|
|
120
|
-
logger.debug(`Fetching Merkle proof for commitment: ${
|
|
121
|
-
let url = `${RELAYER_API_URL}/merkle/
|
|
125
|
+
logger.debug(`Fetching Merkle proof for commitment: ${commitments.join(', ')} with token: ${tokenName || 'N/A'}`);
|
|
126
|
+
let url = `${RELAYER_API_URL}/merkle/proofv2/?commitments=${commitments.join(',')}`
|
|
122
127
|
if (tokenName) {
|
|
123
|
-
url += '
|
|
128
|
+
url += '&token=' + tokenName
|
|
124
129
|
}
|
|
130
|
+
logger.debug(`Fetching Merkle proof from URL: ${url}`);
|
|
125
131
|
const response = await fetch(url);
|
|
126
132
|
if (!response.ok) {
|
|
127
133
|
throw new Error(`Failed to fetch Merkle proof: ${url}`);
|
|
128
134
|
}
|
|
129
|
-
const data = await response.json()
|
|
130
|
-
logger.debug(`✓ Fetched Merkle proof with ${data.pathElements.length} elements`);
|
|
135
|
+
const data = await response.json();
|
|
131
136
|
return data;
|
|
132
137
|
} catch (error) {
|
|
133
|
-
console.error(`Failed to fetch Merkle proof for
|
|
138
|
+
console.error(`Failed to fetch Merkle proof for commitments ${commitments.join(', ')}:`, error);
|
|
134
139
|
throw error;
|
|
135
140
|
}
|
|
136
141
|
}
|
package/src/withdraw.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as hasher from '@lightprotocol/hasher.rs';
|
|
2
|
+
import { Connection, LAMPORTS_PER_SOL, PublicKey } from '@solana/web3.js';
|
|
2
3
|
import BN from 'bn.js';
|
|
3
4
|
import { Buffer } from 'buffer';
|
|
4
5
|
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
5
|
-
import * as hasher from '@lightprotocol/hasher.rs';
|
|
6
6
|
import { Utxo } from './models/utxo.js';
|
|
7
7
|
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
8
8
|
|
|
9
|
-
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, RELAYER_API_URL
|
|
9
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, RELAYER_API_URL } from './utils/constants.js';
|
|
10
10
|
import { EncryptionService, serializeProofAndExtData } from './utils/encryption.js';
|
|
11
|
-
import { fetchMerkleProof, findNullifierPDAs, getExtDataHash, getProgramAccounts
|
|
11
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getProgramAccounts } from './utils/utils.js';
|
|
12
12
|
|
|
13
|
+
import { getConfig } from './config.js';
|
|
13
14
|
import { getUtxos } from './getUtxos.js';
|
|
14
15
|
import { logger } from './utils/logger.js';
|
|
15
|
-
import { getConfig } from './config.js';
|
|
16
16
|
// Indexer API endpoint
|
|
17
17
|
|
|
18
18
|
|
|
@@ -65,11 +65,6 @@ export async function withdraw({ recipient, lightWasm, storage, publicKey, conne
|
|
|
65
65
|
|
|
66
66
|
const { treeAccount, treeTokenAccount, globalConfigAccount } = getProgramAccounts()
|
|
67
67
|
|
|
68
|
-
// Get current tree state
|
|
69
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState();
|
|
70
|
-
logger.debug(`Using tree root: ${root}`);
|
|
71
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
72
|
-
|
|
73
68
|
// Generate a deterministic private key derived from the wallet keypair
|
|
74
69
|
const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
75
70
|
|
|
@@ -124,23 +119,26 @@ export async function withdraw({ recipient, lightWasm, storage, publicKey, conne
|
|
|
124
119
|
logger.debug(`Withdrawing ${amount_in_lamports} lamports with ${fee_in_lamports} fee, ${changeAmount.toString()} as change`);
|
|
125
120
|
|
|
126
121
|
// Get Merkle proofs for both input UTXOs
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
);
|
|
122
|
+
let commitmentsToFetch = inputs.map(utxo => utxo.getCommitment());
|
|
123
|
+
console.log('commitmentsToFetch', commitmentsToFetch)
|
|
124
|
+
const { root, nextIndex, proofs } = await fetchMerkleProof(commitmentsToFetch);
|
|
125
|
+
// const inputMerkleProofs = await Promise.all(
|
|
126
|
+
// inputs.map(async (utxo, index) => {
|
|
127
|
+
// // For dummy UTXO (amount is 0), use a zero-filled proof
|
|
128
|
+
// if (utxo.amount.eq(new BN(0))) {
|
|
129
|
+
// return {
|
|
130
|
+
// pathElements: [...new Array(MERKLE_TREE_DEPTH).fill("0")],
|
|
131
|
+
// pathIndices: Array(MERKLE_TREE_DEPTH).fill(0)
|
|
132
|
+
// };
|
|
133
|
+
// }
|
|
134
|
+
// // For real UTXOs, fetch the proof from API
|
|
135
|
+
// const commitment = await utxo.getCommitment();
|
|
136
|
+
// return fetchMerkleProof(commitment);
|
|
137
|
+
// })
|
|
138
|
+
// );
|
|
141
139
|
|
|
142
140
|
// Extract path elements and indices
|
|
143
|
-
const inputMerklePathElements =
|
|
141
|
+
const inputMerklePathElements = proofs.map(proof => proof.pathElements);
|
|
144
142
|
const inputMerklePathIndices = inputs.map(utxo => utxo.index || 0);
|
|
145
143
|
|
|
146
144
|
// Create outputs: first output is change, second is dummy (required by protocol)
|
|
@@ -149,13 +147,13 @@ export async function withdraw({ recipient, lightWasm, storage, publicKey, conne
|
|
|
149
147
|
lightWasm,
|
|
150
148
|
amount: changeAmount.toString(),
|
|
151
149
|
keypair: utxoKeypairV2,
|
|
152
|
-
index:
|
|
150
|
+
index: nextIndex
|
|
153
151
|
}), // Change output
|
|
154
152
|
new Utxo({
|
|
155
153
|
lightWasm,
|
|
156
154
|
amount: '0',
|
|
157
155
|
keypair: utxoKeypairV2,
|
|
158
|
-
index:
|
|
156
|
+
index: nextIndex + 1
|
|
159
157
|
}) // Empty UTXO
|
|
160
158
|
];
|
|
161
159
|
|
package/src/withdrawSPL.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as hasher from '@lightprotocol/hasher.rs';
|
|
2
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
3
|
import BN from 'bn.js';
|
|
3
4
|
import { Buffer } from 'buffer';
|
|
4
5
|
import { Keypair as UtxoKeypair } from './models/keypair.js';
|
|
5
|
-
import * as hasher from '@lightprotocol/hasher.rs';
|
|
6
6
|
import { Utxo } from './models/utxo.js';
|
|
7
7
|
import { parseProofToBytesArray, parseToBytesArray, prove } from './utils/prover.js';
|
|
8
8
|
|
|
9
|
-
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE,
|
|
9
|
+
import { ALT_ADDRESS, FEE_RECIPIENT, FIELD_SIZE, PROGRAM_ID, RELAYER_API_URL, tokens } from './utils/constants.js';
|
|
10
10
|
import { EncryptionService, serializeProofAndExtData } from './utils/encryption.js';
|
|
11
|
-
import { fetchMerkleProof,
|
|
11
|
+
import { fetchMerkleProof, findCrossCheckNullifierPDAs, findNullifierPDAs, getExtDataHash, getMintAddressField, getProgramAccounts } from './utils/utils.js';
|
|
12
12
|
|
|
13
|
-
import { getUtxosSPL, isUtxoSpent } from './getUtxosSPL.js';
|
|
14
|
-
import { logger } from './utils/logger.js';
|
|
15
|
-
import { getConfig } from './config.js';
|
|
16
13
|
import { getAssociatedTokenAddressSync, getMint } from '@solana/spl-token';
|
|
14
|
+
import { getConfig } from './config.js';
|
|
15
|
+
import { getUtxosSPL } from './getUtxosSPL.js';
|
|
16
|
+
import { logger } from './utils/logger.js';
|
|
17
17
|
// Indexer API endpoint
|
|
18
18
|
|
|
19
19
|
|
|
@@ -120,11 +120,6 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
120
120
|
|
|
121
121
|
const { globalConfigAccount, treeTokenAccount } = getProgramAccounts()
|
|
122
122
|
|
|
123
|
-
// Get current tree state
|
|
124
|
-
const { root, nextIndex: currentNextIndex } = await queryRemoteTreeState(token.name);
|
|
125
|
-
logger.debug(`Using tree root: ${root}`);
|
|
126
|
-
logger.debug(`New UTXOs will be inserted at indices: ${currentNextIndex} and ${currentNextIndex + 1}`);
|
|
127
|
-
|
|
128
123
|
// Generate a deterministic private key derived from the wallet keypair
|
|
129
124
|
const utxoPrivateKey = encryptionService.deriveUtxoPrivateKey();
|
|
130
125
|
|
|
@@ -180,24 +175,11 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
180
175
|
const changeAmount = totalInputAmount.sub(new BN(base_units)).sub(new BN(fee_base_units));
|
|
181
176
|
logger.debug(`Withdrawing ${base_units} lamports with ${fee_base_units} fee, ${changeAmount.toString()} as change`);
|
|
182
177
|
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
inputs.map(async (utxo, index) => {
|
|
186
|
-
// For dummy UTXO (amount is 0), use a zero-filled proof
|
|
187
|
-
if (utxo.amount.eq(new BN(0))) {
|
|
188
|
-
return {
|
|
189
|
-
pathElements: [...new Array(MERKLE_TREE_DEPTH).fill("0")],
|
|
190
|
-
pathIndices: Array(MERKLE_TREE_DEPTH).fill(0)
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
// For real UTXOs, fetch the proof from API
|
|
194
|
-
const commitment = await utxo.getCommitment();
|
|
195
|
-
return fetchMerkleProof(commitment, token.name);
|
|
196
|
-
})
|
|
197
|
-
);
|
|
178
|
+
let commitmentsToFetch = inputs.map(utxo => utxo.getCommitment());
|
|
179
|
+
const { root, nextIndex, proofs } = await fetchMerkleProof(commitmentsToFetch, token.name);
|
|
198
180
|
|
|
199
181
|
// Extract path elements and indices
|
|
200
|
-
const inputMerklePathElements =
|
|
182
|
+
const inputMerklePathElements = proofs.map(proof => proof.pathElements);
|
|
201
183
|
const inputMerklePathIndices = inputs.map(utxo => utxo.index || 0);
|
|
202
184
|
|
|
203
185
|
// Create outputs: first output is change, second is dummy (required by protocol)
|
|
@@ -206,14 +188,14 @@ export async function withdrawSPL({ recipient, lightWasm, storage, publicKey, co
|
|
|
206
188
|
lightWasm,
|
|
207
189
|
amount: changeAmount.toString(),
|
|
208
190
|
keypair: utxoKeypairV2,
|
|
209
|
-
index:
|
|
191
|
+
index: nextIndex,
|
|
210
192
|
mintAddress: token.pubkey.toString()
|
|
211
193
|
}), // Change output
|
|
212
194
|
new Utxo({
|
|
213
195
|
lightWasm,
|
|
214
196
|
amount: '0',
|
|
215
197
|
keypair: utxoKeypairV2,
|
|
216
|
-
index:
|
|
198
|
+
index: nextIndex + 1,
|
|
217
199
|
mintAddress: token.pubkey.toString()
|
|
218
200
|
}) // Empty UTXO
|
|
219
201
|
];
|