@fairblock/stabletrust 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +304 -0
- package/package.json +41 -0
- package/pkg/LICENSE +176 -0
- package/pkg/confidential_transfer_proof_generation.d.ts +65 -0
- package/pkg/confidential_transfer_proof_generation.js +654 -0
- package/pkg/confidential_transfer_proof_generation_bg.wasm +0 -0
- package/pkg/confidential_transfer_proof_generation_bg.wasm.d.ts +17 -0
- package/pkg/package.json +28 -0
- package/pkg/proof_generator_wasm_bg.wasm +0 -0
- package/src/client.js +885 -0
- package/src/constants.js +32 -0
- package/src/crypto.js +90 -0
- package/src/index.d.ts +275 -0
- package/src/index.js +5 -0
- package/src/utils.js +84 -0
- package/src/wasm-loader.js +85 -0
package/src/constants.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract ABIs and Constants
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const CONTRACT_ABI = [
|
|
6
|
+
"function createConfidentialAccount(bytes elgamalPubkey) external",
|
|
7
|
+
"function deposit(address token, uint256 plainAmount) external",
|
|
8
|
+
"function getAccountCore(address ownerAddr) external view returns ((bool exists, bool finalized, bool hasPendingAction, uint256 lastUpdate, bytes pubkey, bytes availableC1, bytes availableC2, uint64 nonce, uint64 lastProcessedNonce))",
|
|
9
|
+
"function getAvailable(address ownerAddr, address token) external view returns (bytes c1, bytes c2)",
|
|
10
|
+
"function getPending(address ownerAddr, address token) external view returns (bytes c1, bytes c2)",
|
|
11
|
+
"function transferConfidential(address recipient, address token, bytes proof, bool useOffchainVerify) external payable",
|
|
12
|
+
"function withdraw(address token, uint256 plainAmount, bytes proof, bool useOffchainVerify) external",
|
|
13
|
+
"function applyPending() external",
|
|
14
|
+
"function feeAmount() external view returns (uint256)",
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
export const ERC20_ABI = [
|
|
18
|
+
"function approve(address spender, uint256 amount) external returns (bool)",
|
|
19
|
+
"function allowance(address owner, address spender) external view returns (uint256)",
|
|
20
|
+
"function balanceOf(address account) external view returns (uint256)",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
export const DEFAULT_CONFIG = {
|
|
24
|
+
CHAIN_ID: 421614,
|
|
25
|
+
EXPLORER_URL: "https://sepolia.arbiscan.io/tx/",
|
|
26
|
+
RPC_URL: "https://sepolia-rollup.arbitrum.io/rpc",
|
|
27
|
+
CONTRACT_ADDRESS: "0x30bAc8a17DCACbA7f70F305f4ad908C9fd6d3E2E",
|
|
28
|
+
TOKEN_ADDRESS: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const TEMPO_FEE_TOKEN_ADDRESS =
|
|
32
|
+
"0x20c0000000000000000000000000000000000000";
|
package/src/crypto.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ethers } from "ethers";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Derives ElGamal encryption keys deterministically using the user's wallet signature.
|
|
5
|
+
* This ensures that the user's privacy keys stay tied to their Ethereum account.
|
|
6
|
+
*
|
|
7
|
+
* @param {ethers.Wallet} wallet - The wallet to derive keys for
|
|
8
|
+
* @param {Object} config - Configuration object with chainId and contractAddress
|
|
9
|
+
* @param {Function} generateKeypair - The WASM function for key generation
|
|
10
|
+
* @returns {Promise<{publicKey: string, privateKey: string}>}
|
|
11
|
+
*/
|
|
12
|
+
export async function deriveKeys(wallet, config, generateKeypair) {
|
|
13
|
+
const domain = {
|
|
14
|
+
name: "Fairblock",
|
|
15
|
+
version: "1",
|
|
16
|
+
chainId: config.chainId,
|
|
17
|
+
verifyingContract: config.contractAddress,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const types = {
|
|
21
|
+
DeriveElGamalKey: [
|
|
22
|
+
{ name: "purpose", type: "string" },
|
|
23
|
+
{ name: "user", type: "address" },
|
|
24
|
+
{ name: "context", type: "bytes32" },
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const contextHash = ethers.keccak256(
|
|
29
|
+
ethers.solidityPacked(
|
|
30
|
+
["uint256", "address", "address", "string"],
|
|
31
|
+
[config.chainId, config.contractAddress, ethers.ZeroAddress, "main"],
|
|
32
|
+
),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const message = {
|
|
36
|
+
purpose: "homomorphic-key-derive-v1",
|
|
37
|
+
user: wallet.address,
|
|
38
|
+
context: contextHash,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const signature = await wallet.signTypedData(domain, types, message);
|
|
42
|
+
const domainContext = JSON.stringify({
|
|
43
|
+
chainId: config.chainId.toString(),
|
|
44
|
+
verifyingContract: config.contractAddress,
|
|
45
|
+
user: wallet.address,
|
|
46
|
+
purpose: "homomorphic-key-derive-v1",
|
|
47
|
+
version: "1",
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const keypair = JSON.parse(
|
|
51
|
+
generateKeypair(signature.slice(2), domainContext),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
publicKey: keypair.public_key,
|
|
56
|
+
privateKey: keypair.private_key,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Decrypts a ciphertext using the private key
|
|
62
|
+
*
|
|
63
|
+
* @param {string} ciphertext - Base64 encoded ciphertext
|
|
64
|
+
* @param {string} privateKey - The private key for decryption
|
|
65
|
+
* @param {Function} decryptFn - The WASM decrypt function
|
|
66
|
+
* @returns {number} The decrypted amount
|
|
67
|
+
*/
|
|
68
|
+
export function decryptCiphertext(ciphertext, privateKey, decryptFn) {
|
|
69
|
+
try {
|
|
70
|
+
const plainStr = decryptFn(ciphertext, privateKey);
|
|
71
|
+
const result = JSON.parse(plainStr);
|
|
72
|
+
return result.decrypted_amount || 0;
|
|
73
|
+
} catch (e) {
|
|
74
|
+
throw new Error("Decryption failed: " + e.message);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Combines two elliptic curve points into a single ciphertext
|
|
80
|
+
*
|
|
81
|
+
* @param {string} c1 - First component (hex string)
|
|
82
|
+
* @param {string} c2 - Second component (hex string)
|
|
83
|
+
* @returns {string} Base64 encoded combined ciphertext
|
|
84
|
+
*/
|
|
85
|
+
export function combineCiphertext(c1, c2) {
|
|
86
|
+
const combined = new Uint8Array(64);
|
|
87
|
+
combined.set(ethers.getBytes(c1), 0);
|
|
88
|
+
combined.set(ethers.getBytes(c2), 32);
|
|
89
|
+
return Buffer.from(combined).toString("base64");
|
|
90
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
declare module "@fairblock/stabletrust" {
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SDK configuration
|
|
6
|
+
*/
|
|
7
|
+
export interface SdkConfig {
|
|
8
|
+
rpcUrl: string;
|
|
9
|
+
contractAddress: string;
|
|
10
|
+
chainId: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Encryption keys
|
|
15
|
+
*/
|
|
16
|
+
export interface Keys {
|
|
17
|
+
publicKey: string;
|
|
18
|
+
privateKey: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Balance information
|
|
23
|
+
*/
|
|
24
|
+
export interface Balance {
|
|
25
|
+
amount: number;
|
|
26
|
+
ciphertext: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Account options
|
|
31
|
+
*/
|
|
32
|
+
export interface AccountOptions {
|
|
33
|
+
waitForFinalization?: boolean;
|
|
34
|
+
maxAttempts?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Deposit options
|
|
39
|
+
*/
|
|
40
|
+
export interface DepositOptions {
|
|
41
|
+
waitForFinalization?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Transfer options
|
|
46
|
+
*/
|
|
47
|
+
export interface TransferOptions {
|
|
48
|
+
useOffchainVerify?: boolean;
|
|
49
|
+
waitForFinalization?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Apply pending options
|
|
54
|
+
*/
|
|
55
|
+
export interface ApplyPendingOptions {
|
|
56
|
+
waitForFinalization?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Withdraw options
|
|
61
|
+
*/
|
|
62
|
+
export interface WithdrawOptions {
|
|
63
|
+
useOffchainVerify?: boolean;
|
|
64
|
+
waitForFinalization?: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Balance options
|
|
69
|
+
*/
|
|
70
|
+
export interface BalanceOptions {
|
|
71
|
+
type?: "available" | "pending";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Wait for pending balance options
|
|
76
|
+
*/
|
|
77
|
+
export interface WaitForPendingOptions {
|
|
78
|
+
maxAttempts?: number;
|
|
79
|
+
intervalMs?: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Main SDK client class
|
|
84
|
+
* WASM auto-initializes on first use - no manual initialization required!
|
|
85
|
+
*/
|
|
86
|
+
export class ConfidentialTransferClient {
|
|
87
|
+
/**
|
|
88
|
+
* Create a new ConfidentialTransferClient
|
|
89
|
+
* @param rpcUrl RPC endpoint URL
|
|
90
|
+
* @param contractAddress Confidential transfer contract address
|
|
91
|
+
* @param chainId Chain ID
|
|
92
|
+
*/
|
|
93
|
+
constructor(rpcUrl: string, contractAddress: string, chainId?: number);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Derive encryption keys for a wallet
|
|
97
|
+
*/
|
|
98
|
+
deriveKeys(wallet: ethers.Wallet | ethers.Signer): Promise<Keys>;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get account information from the contract
|
|
102
|
+
*/
|
|
103
|
+
getAccountInfo(address: string): Promise<any>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Create a confidential account if it doesn't exist and wait for finalization
|
|
107
|
+
*/
|
|
108
|
+
ensureAccount(
|
|
109
|
+
wallet: ethers.Wallet | ethers.Signer,
|
|
110
|
+
options?: AccountOptions,
|
|
111
|
+
): Promise<Keys>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get decrypted balance for an address
|
|
115
|
+
* @param address Account address
|
|
116
|
+
* @param privateKey Private key for decryption
|
|
117
|
+
* @param tokenAddress Token contract address
|
|
118
|
+
* @param options Balance options
|
|
119
|
+
*/
|
|
120
|
+
getBalance(
|
|
121
|
+
address: string,
|
|
122
|
+
privateKey: string,
|
|
123
|
+
tokenAddress: string,
|
|
124
|
+
options?: BalanceOptions,
|
|
125
|
+
): Promise<Balance>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Deposit tokens into confidential account
|
|
129
|
+
* @param wallet Wallet to deposit from
|
|
130
|
+
* @param tokenAddress Token contract address to deposit
|
|
131
|
+
* @param amount Amount to deposit
|
|
132
|
+
* @param options Deposit options
|
|
133
|
+
*/
|
|
134
|
+
deposit(
|
|
135
|
+
wallet: ethers.Wallet | ethers.Signer,
|
|
136
|
+
tokenAddress: string,
|
|
137
|
+
amount: bigint | string | number,
|
|
138
|
+
options?: DepositOptions,
|
|
139
|
+
): Promise<ethers.ContractTransactionReceipt>;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Transfer confidential tokens to another address
|
|
143
|
+
* All necessary data is derived automatically - you only need the wallet and recipient info
|
|
144
|
+
* @param senderWallet Sender's wallet
|
|
145
|
+
* @param recipientAddress Recipient's address
|
|
146
|
+
* @param tokenAddress Token contract address to transfer
|
|
147
|
+
* @param amount Amount to transfer
|
|
148
|
+
* @param options Transfer options
|
|
149
|
+
*/
|
|
150
|
+
transfer(
|
|
151
|
+
senderWallet: ethers.Wallet | ethers.Signer,
|
|
152
|
+
recipientAddress: string,
|
|
153
|
+
tokenAddress: string,
|
|
154
|
+
amount: number,
|
|
155
|
+
options?: TransferOptions,
|
|
156
|
+
): Promise<ethers.ContractTransactionReceipt>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Apply pending balance to available balance
|
|
160
|
+
*/
|
|
161
|
+
applyPending(
|
|
162
|
+
wallet: ethers.Wallet | ethers.Signer,
|
|
163
|
+
options?: ApplyPendingOptions,
|
|
164
|
+
): Promise<ethers.ContractTransactionReceipt>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Withdraw confidential tokens to public ERC20
|
|
168
|
+
* @param wallet Wallet to withdraw to
|
|
169
|
+
* @param tokenAddress Token contract address to withdraw
|
|
170
|
+
* @param amount Amount to withdraw
|
|
171
|
+
* @param keys Encryption keys
|
|
172
|
+
* @param currentBalanceCiphertext Current balance ciphertext
|
|
173
|
+
* @param currentBalance Current balance (decrypted)
|
|
174
|
+
* @param options Withdrawal options
|
|
175
|
+
*/
|
|
176
|
+
withdraw(
|
|
177
|
+
wallet: ethers.Wallet | ethers.Signer,
|
|
178
|
+
tokenAddress: string,
|
|
179
|
+
amount: number,
|
|
180
|
+
keys: Keys,
|
|
181
|
+
currentBalanceCiphertext: string,
|
|
182
|
+
currentBalance: number,
|
|
183
|
+
options?: WithdrawOptions,
|
|
184
|
+
): Promise<ethers.ContractTransactionReceipt>;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Wait for pending balance to appear
|
|
188
|
+
* @param address Account address
|
|
189
|
+
* @param privateKey Private key for decryption
|
|
190
|
+
* @param tokenAddress Token contract address
|
|
191
|
+
* @param options Wait options
|
|
192
|
+
*/
|
|
193
|
+
waitForPendingBalance(
|
|
194
|
+
address: string,
|
|
195
|
+
privateKey: string,
|
|
196
|
+
tokenAddress: string,
|
|
197
|
+
options?: WaitForPendingOptions,
|
|
198
|
+
): Promise<Balance>;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get the current fee amount for confidential transfers
|
|
202
|
+
*/
|
|
203
|
+
getFeeAmount(): Promise<bigint>;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get ERC20 token balance
|
|
207
|
+
* @param address Account address
|
|
208
|
+
* @param tokenAddress Token contract address
|
|
209
|
+
*/
|
|
210
|
+
getTokenBalance(address: string, tokenAddress: string): Promise<bigint>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Cryptography utilities
|
|
215
|
+
*/
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Derive encryption keys for a wallet
|
|
219
|
+
*/
|
|
220
|
+
export function deriveKeys(
|
|
221
|
+
wallet: ethers.Wallet | ethers.Signer,
|
|
222
|
+
domainContext: { chainId: number; contractAddress: string },
|
|
223
|
+
generateKeypairFn: (signature: string, context: string) => string,
|
|
224
|
+
): Promise<Keys>;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Decrypt a ciphertext using a private key
|
|
228
|
+
*/
|
|
229
|
+
export function decryptCiphertext(
|
|
230
|
+
ciphertext: string,
|
|
231
|
+
privateKey: string,
|
|
232
|
+
decryptFn: (ciphertext: string, privateKey: string) => string,
|
|
233
|
+
): number;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Combine two ciphertext parts (c1, c2)
|
|
237
|
+
*/
|
|
238
|
+
export function combineCiphertext(c1: string, c2: string): string;
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Utilities
|
|
242
|
+
*/
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Encode a transfer proof for contract submission
|
|
246
|
+
*/
|
|
247
|
+
export function encodeTransferProof(proof: any): string;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Encode a withdraw proof for contract submission
|
|
251
|
+
*/
|
|
252
|
+
export function encodeWithdrawProof(proof: any): string;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Constants
|
|
256
|
+
*/
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Contract ABI
|
|
260
|
+
*/
|
|
261
|
+
export const CONTRACT_ABI: any[];
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* ERC20 ABI
|
|
265
|
+
*/
|
|
266
|
+
export const ERC20_ABI: any[];
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Default configuration values
|
|
270
|
+
*/
|
|
271
|
+
export const DEFAULT_CONFIG: {
|
|
272
|
+
CHAIN_ID: number;
|
|
273
|
+
EXPLORER_URL: string;
|
|
274
|
+
};
|
|
275
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ConfidentialTransferClient } from "./client.js";
|
|
2
|
+
export { deriveKeys, decryptCiphertext, combineCiphertext } from "./crypto.js";
|
|
3
|
+
export { encodeTransferProof, encodeWithdrawProof } from "./utils.js";
|
|
4
|
+
export { CONTRACT_ABI, ERC20_ABI, DEFAULT_CONFIG } from "./constants.js";
|
|
5
|
+
// Note: initializeWasm is now internal - WASM auto-initializes on first client use
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ethers } from "ethers";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Encodes the ZK-Proof data for a transfer into a format the Solidity contract expects.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} proofData - The proof data object
|
|
7
|
+
* @returns {string} Encoded proof bytes
|
|
8
|
+
*/
|
|
9
|
+
export function encodeTransferProof(proofData) {
|
|
10
|
+
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
|
|
11
|
+
return abiCoder.encode(
|
|
12
|
+
["string", "string", "string"],
|
|
13
|
+
[
|
|
14
|
+
proofData.equality_proof,
|
|
15
|
+
proofData.ciphertext_validity_proof,
|
|
16
|
+
proofData.range_proof,
|
|
17
|
+
],
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Encodes the ZK-Proof data for a withdrawal.
|
|
23
|
+
*
|
|
24
|
+
* @param {Object} proofData - The proof data object
|
|
25
|
+
* @returns {string} Encoded proof bytes
|
|
26
|
+
*/
|
|
27
|
+
export function encodeWithdrawProof(proofData) {
|
|
28
|
+
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
|
|
29
|
+
return abiCoder.encode(
|
|
30
|
+
["string", "string"],
|
|
31
|
+
[proofData.equality_proof, proofData.range_proof],
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Delays execution for a specified time
|
|
37
|
+
*
|
|
38
|
+
* @param {number} ms - Milliseconds to wait
|
|
39
|
+
* @returns {Promise<void>}
|
|
40
|
+
*/
|
|
41
|
+
export function sleep(ms) {
|
|
42
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Logs a transaction with optional privacy note
|
|
47
|
+
*
|
|
48
|
+
* @param {string} hash - Transaction hash
|
|
49
|
+
* @param {string} explorerUrl - Block explorer base URL
|
|
50
|
+
* @param {boolean} isConfidential - Whether the transaction is confidential
|
|
51
|
+
*/
|
|
52
|
+
export function logTransaction(hash, explorerUrl, isConfidential = false) {
|
|
53
|
+
console.log(`Transaction submitted: ${explorerUrl}${hash}`);
|
|
54
|
+
if (isConfidential) {
|
|
55
|
+
console.log(
|
|
56
|
+
`Note: This is a confidential transaction - the amount is not visible on-chain.`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Waits for a condition to be true with timeout
|
|
63
|
+
*
|
|
64
|
+
* @param {Function} conditionFn - Async function that returns boolean
|
|
65
|
+
* @param {number} maxAttempts - Maximum number of attempts
|
|
66
|
+
* @param {number} intervalMs - Interval between attempts in milliseconds
|
|
67
|
+
* @param {string} actionLabel - Label for logging
|
|
68
|
+
* @returns {Promise<void>}
|
|
69
|
+
* @throws {Error} If timeout is reached
|
|
70
|
+
*/
|
|
71
|
+
export async function waitForCondition(
|
|
72
|
+
conditionFn,
|
|
73
|
+
maxAttempts = 60,
|
|
74
|
+
intervalMs = 3000,
|
|
75
|
+
actionLabel = "operation",
|
|
76
|
+
) {
|
|
77
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
78
|
+
if (await conditionFn()) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await sleep(intervalMs);
|
|
82
|
+
}
|
|
83
|
+
throw new Error(`Timeout waiting for ${actionLabel}`);
|
|
84
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import init, {
|
|
5
|
+
generate_deterministic_keypair,
|
|
6
|
+
generate_transfer_proof,
|
|
7
|
+
generate_withdraw_proof,
|
|
8
|
+
decrypt_ciphertext,
|
|
9
|
+
} from "../pkg/confidential_transfer_proof_generation.js";
|
|
10
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
|
|
14
|
+
let isInitialized = false;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Initialize the WASM module
|
|
18
|
+
* This must be called before using the SDK
|
|
19
|
+
*
|
|
20
|
+
* @param {Buffer|Uint8Array|string} [wasmPath] - Optional custom WASM file path or buffer
|
|
21
|
+
* @returns {Promise<Object>} WASM module functions
|
|
22
|
+
*/
|
|
23
|
+
export async function initializeWasm(wasmPath) {
|
|
24
|
+
if (isInitialized) {
|
|
25
|
+
return {
|
|
26
|
+
generate_deterministic_keypair,
|
|
27
|
+
generate_transfer_proof,
|
|
28
|
+
generate_withdraw_proof,
|
|
29
|
+
decrypt_ciphertext,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
let wasmBuffer;
|
|
35
|
+
|
|
36
|
+
if (wasmPath) {
|
|
37
|
+
// If a custom path or buffer is provided
|
|
38
|
+
if (typeof wasmPath === "string") {
|
|
39
|
+
wasmBuffer = fs.readFileSync(wasmPath);
|
|
40
|
+
} else {
|
|
41
|
+
wasmBuffer = wasmPath;
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
// Use the bundled WASM file
|
|
45
|
+
const defaultWasmPath = path.resolve(
|
|
46
|
+
__dirname,
|
|
47
|
+
"../pkg/confidential_transfer_proof_generation_bg.wasm",
|
|
48
|
+
);
|
|
49
|
+
wasmBuffer = fs.readFileSync(defaultWasmPath);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await init(wasmBuffer);
|
|
53
|
+
isInitialized = true;
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
generate_deterministic_keypair,
|
|
57
|
+
generate_transfer_proof,
|
|
58
|
+
generate_withdraw_proof,
|
|
59
|
+
decrypt_ciphertext,
|
|
60
|
+
};
|
|
61
|
+
} catch (error) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Failed to initialize WASM module: ${error.message}. ` +
|
|
64
|
+
`Make sure the WASM file exists at the expected location.`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check if WASM module is initialized
|
|
71
|
+
* @returns {boolean}
|
|
72
|
+
*/
|
|
73
|
+
export function isWasmInitialized() {
|
|
74
|
+
return isInitialized;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Export WASM functions (they will only work after initialization)
|
|
79
|
+
*/
|
|
80
|
+
export {
|
|
81
|
+
generate_deterministic_keypair,
|
|
82
|
+
generate_transfer_proof,
|
|
83
|
+
generate_withdraw_proof,
|
|
84
|
+
decrypt_ciphertext,
|
|
85
|
+
};
|