@zama-fhe/sdk 1.0.0-alpha.10
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/LICENSE +28 -0
- package/README.md +801 -0
- package/dist/chunk-5QJTGZHY.js +101 -0
- package/dist/chunk-5QJTGZHY.js.map +1 -0
- package/dist/chunk-6JRD26PS.js +114 -0
- package/dist/chunk-6JRD26PS.js.map +1 -0
- package/dist/chunk-PHE3BSIB.js +5143 -0
- package/dist/chunk-PHE3BSIB.js.map +1 -0
- package/dist/chunk-UF47M3QR.js +32 -0
- package/dist/chunk-UF47M3QR.js.map +1 -0
- package/dist/chunk-WYWAO3QE.js +182 -0
- package/dist/chunk-WYWAO3QE.js.map +1 -0
- package/dist/cleartext/index.d.ts +45 -0
- package/dist/cleartext/index.js +522 -0
- package/dist/cleartext/index.js.map +1 -0
- package/dist/ethers/index.d.ts +86 -0
- package/dist/ethers/index.js +148 -0
- package/dist/ethers/index.js.map +1 -0
- package/dist/index.d.ts +33405 -0
- package/dist/index.js +3563 -0
- package/dist/index.js.map +1 -0
- package/dist/node/index.d.ts +195 -0
- package/dist/node/index.js +337 -0
- package/dist/node/index.js.map +1 -0
- package/dist/relayer-sdk-Dh9aQmBm.d.ts +39 -0
- package/dist/relayer-sdk.node-worker.d.ts +2 -0
- package/dist/relayer-sdk.node-worker.js +348 -0
- package/dist/relayer-sdk.node-worker.js.map +1 -0
- package/dist/relayer-sdk.types-CgHZ6qZn.d.ts +327 -0
- package/dist/relayer-sdk.worker.js +511 -0
- package/dist/relayer-sdk.worker.js.map +1 -0
- package/dist/relayer-utils-phBmWrNB.d.ts +10 -0
- package/dist/token.types-CUTkehsp.d.ts +299 -0
- package/dist/transfer-batcher-CNtrNMz6.d.ts +197 -0
- package/dist/viem/index.d.ts +58 -0
- package/dist/viem/index.js +143 -0
- package/dist/viem/index.js.map +1 -0
- package/package.json +90 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3563 @@
|
|
|
1
|
+
import { assertObject, assertString, assertArray, normalizeAddress, supportsInterfaceContract, ERC7984_INTERFACE_ID, ERC7984_WRAPPER_INTERFACE_ID, pLimit, wrapperExistsContract, getWrapperContract, underlyingContract, allowanceContract, nameContract, symbolContract, decimalsContract, confidentialBalanceOfContract, confidentialTransferContract, confidentialTransferFromContract, setOperatorContract, isOperatorContract, wrapContract, wrapETHContract, unwrapContract, unwrapFromBalanceContract, finalizeUnwrapContract, approveContract } from './chunk-PHE3BSIB.js';
|
|
2
|
+
export { DEPLOYMENT_COORDINATOR_ABI, ENCRYPTION_ABI, ERC165_ABI, ERC20_ABI, ERC20_METADATA_ABI, ERC7984_INTERFACE_ID, ERC7984_WRAPPER_INTERFACE_ID, FEE_MANAGER_ABI, FHE_GAS_LIMIT, TRANSFER_BATCHER_ABI, WRAPPER_ABI, allowanceContract, approveContract, balanceOfContract, confidentialBalanceOfContract, confidentialBatchTransferContract, confidentialTotalSupplyContract, confidentialTransferContract, confidentialTransferFromContract, decimalsContract, deploymentCoordinatorContract, finalizeUnwrapContract, getBatchTransferFeeContract, getFeeRecipientContract, getUnwrapFeeContract, getWrapFeeContract, getWrapperContract, isConfidentialTokenContract, isConfidentialWrapperContract, isFinalizeUnwrapOperatorContract, isOperatorContract, nameContract, rateContract, setFinalizeUnwrapOperatorContract, setOperatorContract, supportsInterfaceContract, symbolContract, totalSupplyContract, underlyingContract, unwrapContract, unwrapFromBalanceContract, wrapContract, wrapETHContract, wrapperExistsContract } from './chunk-PHE3BSIB.js';
|
|
3
|
+
import { BaseWorkerClient } from './chunk-WYWAO3QE.js';
|
|
4
|
+
import { ZamaError, EncryptionFailedError, SigningRejectedError, SigningFailedError, DecryptionFailedError, NoCiphertextError, RelayerRequestFailedError, TransactionRevertedError, ApprovalFailedError } from './chunk-6JRD26PS.js';
|
|
5
|
+
export { ApprovalFailedError, ConfigurationError, CredentialExpiredError, DecryptionFailedError, EncryptionFailedError, InvalidCredentialsError, NoCiphertextError, RelayerRequestFailedError, SigningFailedError, SigningRejectedError, TransactionRevertedError, ZamaError, ZamaErrorCode, matchZamaError } from './chunk-6JRD26PS.js';
|
|
6
|
+
import { mergeFhevmConfig, buildEIP712DomainType, withRetry } from './chunk-5QJTGZHY.js';
|
|
7
|
+
export { HardhatConfig, MainnetConfig, SepoliaConfig } from './chunk-5QJTGZHY.js';
|
|
8
|
+
|
|
9
|
+
// src/worker/worker.client.ts
|
|
10
|
+
var RelayerWorkerClient = class extends BaseWorkerClient {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super(config, config.logger);
|
|
13
|
+
}
|
|
14
|
+
createWorker() {
|
|
15
|
+
return new Worker(new URL("./relayer-sdk.worker.js", import.meta.url));
|
|
16
|
+
}
|
|
17
|
+
wireEvents(worker) {
|
|
18
|
+
worker.onmessage = (event) => this.handleResponse(event.data);
|
|
19
|
+
worker.onerror = (event) => this.handleWorkerError(event.message);
|
|
20
|
+
worker.onmessageerror = () => this.handleWorkerMessageError();
|
|
21
|
+
}
|
|
22
|
+
postMessage(worker, request) {
|
|
23
|
+
worker.postMessage(request);
|
|
24
|
+
}
|
|
25
|
+
terminateWorker(worker) {
|
|
26
|
+
worker.terminate();
|
|
27
|
+
}
|
|
28
|
+
generateRequestId() {
|
|
29
|
+
return crypto.randomUUID();
|
|
30
|
+
}
|
|
31
|
+
getInitPayload() {
|
|
32
|
+
return { type: "INIT", payload: this.config };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Update the CSRF token in the worker.
|
|
36
|
+
* Call this before making authenticated requests to ensure the token is fresh.
|
|
37
|
+
*/
|
|
38
|
+
async updateCsrf(csrfToken) {
|
|
39
|
+
await this.sendRequest("UPDATE_CSRF", { csrfToken });
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// src/relayer/relayer-web.ts
|
|
44
|
+
var RELAYER_SDK_VERSION = "0.4.1";
|
|
45
|
+
var CDN_URL = `https://cdn.zama.org/relayer-sdk-js/${RELAYER_SDK_VERSION}/relayer-sdk-js.umd.cjs`;
|
|
46
|
+
var CDN_INTEGRITY = "2bd5401738b74509549bed2029bbbabedd481b10ac260f66e64a4ff3723d6d704180c51e882757c56ca1840491e90e33";
|
|
47
|
+
var RelayerWeb = class {
|
|
48
|
+
#workerClient = null;
|
|
49
|
+
#initPromise = null;
|
|
50
|
+
#ensureLock = null;
|
|
51
|
+
#terminated = false;
|
|
52
|
+
#resolvedChainId = null;
|
|
53
|
+
#status = "idle";
|
|
54
|
+
#initError;
|
|
55
|
+
#config;
|
|
56
|
+
constructor(config) {
|
|
57
|
+
this.#config = config;
|
|
58
|
+
}
|
|
59
|
+
/** Current WASM initialization status. */
|
|
60
|
+
get status() {
|
|
61
|
+
return this.#status;
|
|
62
|
+
}
|
|
63
|
+
/** The error that caused initialization to fail, if `status` is `"error"`. */
|
|
64
|
+
get initError() {
|
|
65
|
+
return this.#initError;
|
|
66
|
+
}
|
|
67
|
+
#setStatus(status, error) {
|
|
68
|
+
this.#status = status;
|
|
69
|
+
this.#initError = error;
|
|
70
|
+
this.#config.onStatusChange?.(status, error);
|
|
71
|
+
}
|
|
72
|
+
async #getWorkerConfig() {
|
|
73
|
+
const chainId = await this.#config.getChainId();
|
|
74
|
+
const { transports, security, threads } = this.#config;
|
|
75
|
+
if (threads !== void 0 && (!Number.isInteger(threads) || threads < 1)) {
|
|
76
|
+
throw new Error(`Invalid thread count: ${threads}. Must be a positive integer.`);
|
|
77
|
+
}
|
|
78
|
+
if (threads !== void 0 && typeof globalThis.SharedArrayBuffer === "undefined") {
|
|
79
|
+
this.#config.logger?.warn(
|
|
80
|
+
"threads option requires SharedArrayBuffer (COOP/COEP headers). Falling back to single-threaded."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
cdnUrl: CDN_URL,
|
|
85
|
+
fhevmConfig: mergeFhevmConfig(chainId, transports[chainId]),
|
|
86
|
+
csrfToken: security?.getCsrfToken?.() ?? "",
|
|
87
|
+
integrity: security?.integrityCheck === false ? void 0 : CDN_INTEGRITY,
|
|
88
|
+
logger: this.#config.logger,
|
|
89
|
+
// Public API uses `threads` (plural, "how many threads"); upstream
|
|
90
|
+
// `initSDK` expects `thread` (singular) — rename at the boundary.
|
|
91
|
+
thread: threads
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Ensure the worker is initialized.
|
|
96
|
+
* Uses a promise lock to prevent concurrent initialization.
|
|
97
|
+
* Resets on failure to allow retries.
|
|
98
|
+
*/
|
|
99
|
+
async #ensureWorker() {
|
|
100
|
+
if (this.#ensureLock) return this.#ensureLock;
|
|
101
|
+
this.#ensureLock = this.#ensureWorkerInner();
|
|
102
|
+
try {
|
|
103
|
+
return await this.#ensureLock;
|
|
104
|
+
} finally {
|
|
105
|
+
this.#ensureLock = null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async #ensureWorkerInner() {
|
|
109
|
+
if (this.#terminated) {
|
|
110
|
+
this.#terminated = false;
|
|
111
|
+
this.#workerClient = null;
|
|
112
|
+
this.#initPromise = null;
|
|
113
|
+
this.#resolvedChainId = null;
|
|
114
|
+
}
|
|
115
|
+
const chainId = await this.#config.getChainId();
|
|
116
|
+
if (this.#resolvedChainId !== null && chainId !== this.#resolvedChainId) {
|
|
117
|
+
this.#workerClient?.terminate();
|
|
118
|
+
this.#workerClient = null;
|
|
119
|
+
this.#initPromise = null;
|
|
120
|
+
}
|
|
121
|
+
this.#resolvedChainId = chainId;
|
|
122
|
+
if (!this.#initPromise) {
|
|
123
|
+
this.#setStatus("initializing");
|
|
124
|
+
this.#initPromise = this.#initWorker().then((client) => {
|
|
125
|
+
this.#setStatus("ready");
|
|
126
|
+
return client;
|
|
127
|
+
}).catch((error) => {
|
|
128
|
+
this.#initPromise = null;
|
|
129
|
+
const wrappedError = error instanceof ZamaError ? error : new EncryptionFailedError("Failed to initialize FHE worker", {
|
|
130
|
+
cause: error instanceof Error ? error : void 0
|
|
131
|
+
});
|
|
132
|
+
this.#setStatus("error", wrappedError);
|
|
133
|
+
throw wrappedError;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
return this.#initPromise;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Initialize the worker (called once via promise lock).
|
|
140
|
+
*/
|
|
141
|
+
async #initWorker() {
|
|
142
|
+
const workerConfig = await this.#getWorkerConfig();
|
|
143
|
+
const client = new RelayerWorkerClient(workerConfig);
|
|
144
|
+
await client.initWorker();
|
|
145
|
+
if (this.#terminated) {
|
|
146
|
+
client.terminate();
|
|
147
|
+
throw new Error("RelayerWeb was terminated during initialization");
|
|
148
|
+
}
|
|
149
|
+
this.#workerClient = client;
|
|
150
|
+
return client;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Terminate the worker and clean up resources.
|
|
154
|
+
* Call this when the SDK is no longer needed.
|
|
155
|
+
*/
|
|
156
|
+
terminate() {
|
|
157
|
+
this.#terminated = true;
|
|
158
|
+
if (this.#workerClient) {
|
|
159
|
+
this.#workerClient.terminate();
|
|
160
|
+
this.#workerClient = null;
|
|
161
|
+
}
|
|
162
|
+
this.#initPromise = null;
|
|
163
|
+
this.#ensureLock = null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Refresh the CSRF token in the worker.
|
|
167
|
+
* Call this before making authenticated network requests.
|
|
168
|
+
*/
|
|
169
|
+
async #refreshCsrfToken() {
|
|
170
|
+
if (this.#workerClient) {
|
|
171
|
+
const token = this.#config.security?.getCsrfToken?.() ?? "";
|
|
172
|
+
if (token) {
|
|
173
|
+
await this.#workerClient.updateCsrf(token);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Generate a keypair for FHE operations.
|
|
179
|
+
*/
|
|
180
|
+
async generateKeypair() {
|
|
181
|
+
const worker = await this.#ensureWorker();
|
|
182
|
+
const result = await worker.generateKeypair();
|
|
183
|
+
return {
|
|
184
|
+
publicKey: result.publicKey,
|
|
185
|
+
privateKey: result.privateKey
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Create EIP712 typed data for user decryption authorization.
|
|
190
|
+
*/
|
|
191
|
+
async createEIP712(publicKey, contractAddresses, startTimestamp, durationDays = 7) {
|
|
192
|
+
const worker = await this.#ensureWorker();
|
|
193
|
+
const result = await worker.createEIP712({
|
|
194
|
+
publicKey,
|
|
195
|
+
contractAddresses,
|
|
196
|
+
startTimestamp,
|
|
197
|
+
durationDays
|
|
198
|
+
});
|
|
199
|
+
const domain = {
|
|
200
|
+
name: result.domain.name,
|
|
201
|
+
version: result.domain.version,
|
|
202
|
+
chainId: result.domain.chainId,
|
|
203
|
+
verifyingContract: result.domain.verifyingContract
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
domain,
|
|
207
|
+
types: {
|
|
208
|
+
EIP712Domain: buildEIP712DomainType(domain),
|
|
209
|
+
UserDecryptRequestVerification: result.types.UserDecryptRequestVerification
|
|
210
|
+
},
|
|
211
|
+
message: {
|
|
212
|
+
publicKey: result.message.publicKey,
|
|
213
|
+
contractAddresses: result.message.contractAddresses,
|
|
214
|
+
startTimestamp: result.message.startTimestamp,
|
|
215
|
+
durationDays: result.message.durationDays,
|
|
216
|
+
extraData: result.message.extraData
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Encrypt values for use in smart contract calls.
|
|
222
|
+
* Each value must specify its FHE type (ebool, euint4–256, eaddress).
|
|
223
|
+
*/
|
|
224
|
+
async encrypt(params) {
|
|
225
|
+
const { values, contractAddress, userAddress } = params;
|
|
226
|
+
return withRetry(async () => {
|
|
227
|
+
const worker = await this.#ensureWorker();
|
|
228
|
+
await this.#refreshCsrfToken();
|
|
229
|
+
const result = await worker.encrypt({ values, contractAddress, userAddress });
|
|
230
|
+
return { handles: result.handles, inputProof: result.inputProof };
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Decrypt ciphertexts using user's private key.
|
|
235
|
+
* Requires a valid EIP712 signature.
|
|
236
|
+
*/
|
|
237
|
+
async userDecrypt(params) {
|
|
238
|
+
return withRetry(async () => {
|
|
239
|
+
const worker = await this.#ensureWorker();
|
|
240
|
+
await this.#refreshCsrfToken();
|
|
241
|
+
const result = await worker.userDecrypt(params);
|
|
242
|
+
return result.clearValues;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Public decryption - no authorization needed.
|
|
247
|
+
* Used for publicly visible encrypted values.
|
|
248
|
+
*/
|
|
249
|
+
async publicDecrypt(handles) {
|
|
250
|
+
return withRetry(async () => {
|
|
251
|
+
const worker = await this.#ensureWorker();
|
|
252
|
+
await this.#refreshCsrfToken();
|
|
253
|
+
const result = await worker.publicDecrypt(handles);
|
|
254
|
+
return {
|
|
255
|
+
clearValues: result.clearValues,
|
|
256
|
+
abiEncodedClearValues: result.abiEncodedClearValues,
|
|
257
|
+
decryptionProof: result.decryptionProof
|
|
258
|
+
};
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Create EIP712 typed data for delegated user decryption authorization.
|
|
263
|
+
*/
|
|
264
|
+
async createDelegatedUserDecryptEIP712(publicKey, contractAddresses, delegatorAddress, startTimestamp, durationDays = 7) {
|
|
265
|
+
const worker = await this.#ensureWorker();
|
|
266
|
+
return worker.createDelegatedUserDecryptEIP712({
|
|
267
|
+
publicKey,
|
|
268
|
+
contractAddresses,
|
|
269
|
+
delegatorAddress,
|
|
270
|
+
startTimestamp,
|
|
271
|
+
durationDays
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Decrypt ciphertexts via delegation.
|
|
276
|
+
* Requires a valid EIP712 signature from the delegator.
|
|
277
|
+
*/
|
|
278
|
+
async delegatedUserDecrypt(params) {
|
|
279
|
+
return withRetry(async () => {
|
|
280
|
+
const worker = await this.#ensureWorker();
|
|
281
|
+
await this.#refreshCsrfToken();
|
|
282
|
+
const result = await worker.delegatedUserDecrypt(params);
|
|
283
|
+
return result.clearValues;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Submit a ZK proof to the relayer for verification.
|
|
288
|
+
*/
|
|
289
|
+
async requestZKProofVerification(zkProof) {
|
|
290
|
+
return withRetry(async () => {
|
|
291
|
+
const worker = await this.#ensureWorker();
|
|
292
|
+
await this.#refreshCsrfToken();
|
|
293
|
+
return worker.requestZKProofVerification(zkProof);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Get the TFHE compact public key.
|
|
298
|
+
*/
|
|
299
|
+
async getPublicKey() {
|
|
300
|
+
const worker = await this.#ensureWorker();
|
|
301
|
+
return (await worker.getPublicKey()).result;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Get public parameters for encryption capacity.
|
|
305
|
+
*/
|
|
306
|
+
async getPublicParams(bits) {
|
|
307
|
+
const worker = await this.#ensureWorker();
|
|
308
|
+
return (await worker.getPublicParams(bits)).result;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// src/abi/batch-swap.abi.ts
|
|
313
|
+
var BATCH_SWAP_ABI = [
|
|
314
|
+
{
|
|
315
|
+
inputs: [
|
|
316
|
+
{
|
|
317
|
+
internalType: "contract DeploymentCoordinator",
|
|
318
|
+
name: "coordinator_",
|
|
319
|
+
type: "address"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
internalType: "contract RegulatedERC7984Upgradeable",
|
|
323
|
+
name: "fromToken_",
|
|
324
|
+
type: "address"
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
internalType: "contract RegulatedERC7984Upgradeable",
|
|
328
|
+
name: "toToken_",
|
|
329
|
+
type: "address"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
internalType: "contract ConfidentialWrapper",
|
|
333
|
+
name: "fromWrapper_",
|
|
334
|
+
type: "address"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
internalType: "contract ConfidentialWrapper",
|
|
338
|
+
name: "toWrapper_",
|
|
339
|
+
type: "address"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
internalType: "uint256",
|
|
343
|
+
name: "minBatchDuration_",
|
|
344
|
+
type: "uint256"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
internalType: "uint256",
|
|
348
|
+
name: "batchExpiry_",
|
|
349
|
+
type: "uint256"
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
internalType: "contract ISwapRoute",
|
|
353
|
+
name: "swapRoute_",
|
|
354
|
+
type: "address"
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
internalType: "uint256",
|
|
358
|
+
name: "minDepositors_",
|
|
359
|
+
type: "uint256"
|
|
360
|
+
}
|
|
361
|
+
],
|
|
362
|
+
stateMutability: "nonpayable",
|
|
363
|
+
type: "constructor"
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
inputs: [],
|
|
367
|
+
name: "AlreadyClaimed",
|
|
368
|
+
type: "error"
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
inputs: [],
|
|
372
|
+
name: "BatchExpiredBeforeDispatch",
|
|
373
|
+
type: "error"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
inputs: [],
|
|
377
|
+
name: "BatchNotDispatched",
|
|
378
|
+
type: "error"
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
inputs: [],
|
|
382
|
+
name: "BatchNotExpiredOrFailed",
|
|
383
|
+
type: "error"
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
inputs: [],
|
|
387
|
+
name: "BatchNotFinalized",
|
|
388
|
+
type: "error"
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
inputs: [],
|
|
392
|
+
name: "BatchNotOpen",
|
|
393
|
+
type: "error"
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
inputs: [],
|
|
397
|
+
name: "ExchangeRateOverflow",
|
|
398
|
+
type: "error"
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
inputs: [],
|
|
402
|
+
name: "FinalizeNotSupported",
|
|
403
|
+
type: "error"
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
inputs: [],
|
|
407
|
+
name: "InsufficientEntropy",
|
|
408
|
+
type: "error"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
inputs: [],
|
|
412
|
+
name: "InvalidBatchExpiry",
|
|
413
|
+
type: "error"
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
inputs: [],
|
|
417
|
+
name: "InvalidFromToken",
|
|
418
|
+
type: "error"
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
inputs: [],
|
|
422
|
+
name: "InvalidMantissa",
|
|
423
|
+
type: "error"
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
inputs: [],
|
|
427
|
+
name: "MinBatchDurationNotMet",
|
|
428
|
+
type: "error"
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
inputs: [],
|
|
432
|
+
name: "NoDeposit",
|
|
433
|
+
type: "error"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
inputs: [
|
|
437
|
+
{
|
|
438
|
+
internalType: "address",
|
|
439
|
+
name: "owner",
|
|
440
|
+
type: "address"
|
|
441
|
+
}
|
|
442
|
+
],
|
|
443
|
+
name: "OwnableInvalidOwner",
|
|
444
|
+
type: "error"
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
inputs: [
|
|
448
|
+
{
|
|
449
|
+
internalType: "address",
|
|
450
|
+
name: "account",
|
|
451
|
+
type: "address"
|
|
452
|
+
}
|
|
453
|
+
],
|
|
454
|
+
name: "OwnableUnauthorizedAccount",
|
|
455
|
+
type: "error"
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
inputs: [],
|
|
459
|
+
name: "RouteInputMismatch",
|
|
460
|
+
type: "error"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
inputs: [],
|
|
464
|
+
name: "RouteOutputMismatch",
|
|
465
|
+
type: "error"
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
inputs: [
|
|
469
|
+
{
|
|
470
|
+
internalType: "address",
|
|
471
|
+
name: "token",
|
|
472
|
+
type: "address"
|
|
473
|
+
}
|
|
474
|
+
],
|
|
475
|
+
name: "SafeERC20FailedOperation",
|
|
476
|
+
type: "error"
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
inputs: [],
|
|
480
|
+
name: "SenderNotAllowed",
|
|
481
|
+
type: "error"
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
inputs: [],
|
|
485
|
+
name: "TokenTransferFailed",
|
|
486
|
+
type: "error"
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
inputs: [],
|
|
490
|
+
name: "UnknownWrapper",
|
|
491
|
+
type: "error"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
inputs: [],
|
|
495
|
+
name: "ZamaProtocolUnsupported",
|
|
496
|
+
type: "error"
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
inputs: [],
|
|
500
|
+
name: "ZeroAddress",
|
|
501
|
+
type: "error"
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
inputs: [],
|
|
505
|
+
name: "ZeroCTokenAmount",
|
|
506
|
+
type: "error"
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
anonymous: false,
|
|
510
|
+
inputs: [
|
|
511
|
+
{
|
|
512
|
+
indexed: true,
|
|
513
|
+
internalType: "uint256",
|
|
514
|
+
name: "batchId",
|
|
515
|
+
type: "uint256"
|
|
516
|
+
},
|
|
517
|
+
{
|
|
518
|
+
indexed: false,
|
|
519
|
+
internalType: "uint256",
|
|
520
|
+
name: "plaintextAmount",
|
|
521
|
+
type: "uint256"
|
|
522
|
+
}
|
|
523
|
+
],
|
|
524
|
+
name: "BatchDispatchCallback",
|
|
525
|
+
type: "event"
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
anonymous: false,
|
|
529
|
+
inputs: [
|
|
530
|
+
{
|
|
531
|
+
indexed: true,
|
|
532
|
+
internalType: "uint256",
|
|
533
|
+
name: "batchId",
|
|
534
|
+
type: "uint256"
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
indexed: false,
|
|
538
|
+
internalType: "uint256",
|
|
539
|
+
name: "newBatchId",
|
|
540
|
+
type: "uint256"
|
|
541
|
+
}
|
|
542
|
+
],
|
|
543
|
+
name: "BatchDispatched",
|
|
544
|
+
type: "event"
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
anonymous: false,
|
|
548
|
+
inputs: [
|
|
549
|
+
{
|
|
550
|
+
indexed: true,
|
|
551
|
+
internalType: "uint256",
|
|
552
|
+
name: "batchId",
|
|
553
|
+
type: "uint256"
|
|
554
|
+
}
|
|
555
|
+
],
|
|
556
|
+
name: "BatchExpired",
|
|
557
|
+
type: "event"
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
anonymous: false,
|
|
561
|
+
inputs: [
|
|
562
|
+
{
|
|
563
|
+
indexed: true,
|
|
564
|
+
internalType: "uint256",
|
|
565
|
+
name: "batchId",
|
|
566
|
+
type: "uint256"
|
|
567
|
+
}
|
|
568
|
+
],
|
|
569
|
+
name: "BatchFailed",
|
|
570
|
+
type: "event"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
anonymous: false,
|
|
574
|
+
inputs: [
|
|
575
|
+
{
|
|
576
|
+
indexed: true,
|
|
577
|
+
internalType: "uint256",
|
|
578
|
+
name: "batchId",
|
|
579
|
+
type: "uint256"
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
indexed: false,
|
|
583
|
+
internalType: "uint64",
|
|
584
|
+
name: "exchangeRate",
|
|
585
|
+
type: "uint64"
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
indexed: false,
|
|
589
|
+
internalType: "uint64",
|
|
590
|
+
name: "mantissa",
|
|
591
|
+
type: "uint64"
|
|
592
|
+
}
|
|
593
|
+
],
|
|
594
|
+
name: "BatchFinalized",
|
|
595
|
+
type: "event"
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
anonymous: false,
|
|
599
|
+
inputs: [
|
|
600
|
+
{
|
|
601
|
+
indexed: true,
|
|
602
|
+
internalType: "uint256",
|
|
603
|
+
name: "batchId",
|
|
604
|
+
type: "uint256"
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
indexed: true,
|
|
608
|
+
internalType: "address",
|
|
609
|
+
name: "user",
|
|
610
|
+
type: "address"
|
|
611
|
+
}
|
|
612
|
+
],
|
|
613
|
+
name: "Claimed",
|
|
614
|
+
type: "event"
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
anonymous: false,
|
|
618
|
+
inputs: [
|
|
619
|
+
{
|
|
620
|
+
indexed: true,
|
|
621
|
+
internalType: "uint256",
|
|
622
|
+
name: "batchId",
|
|
623
|
+
type: "uint256"
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
indexed: false,
|
|
627
|
+
internalType: "uint256",
|
|
628
|
+
name: "startIndex",
|
|
629
|
+
type: "uint256"
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
indexed: false,
|
|
633
|
+
internalType: "uint256",
|
|
634
|
+
name: "endIndex",
|
|
635
|
+
type: "uint256"
|
|
636
|
+
}
|
|
637
|
+
],
|
|
638
|
+
name: "ClaimsPushed",
|
|
639
|
+
type: "event"
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
anonymous: false,
|
|
643
|
+
inputs: [
|
|
644
|
+
{
|
|
645
|
+
indexed: true,
|
|
646
|
+
internalType: "uint256",
|
|
647
|
+
name: "batchId",
|
|
648
|
+
type: "uint256"
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
indexed: true,
|
|
652
|
+
internalType: "address",
|
|
653
|
+
name: "user",
|
|
654
|
+
type: "address"
|
|
655
|
+
}
|
|
656
|
+
],
|
|
657
|
+
name: "Deposited",
|
|
658
|
+
type: "event"
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
anonymous: false,
|
|
662
|
+
inputs: [
|
|
663
|
+
{
|
|
664
|
+
indexed: true,
|
|
665
|
+
internalType: "address",
|
|
666
|
+
name: "operator",
|
|
667
|
+
type: "address"
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
indexed: false,
|
|
671
|
+
internalType: "uint48",
|
|
672
|
+
name: "until",
|
|
673
|
+
type: "uint48"
|
|
674
|
+
}
|
|
675
|
+
],
|
|
676
|
+
name: "FinalizeUnwrapOperatorUpdated",
|
|
677
|
+
type: "event"
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
anonymous: false,
|
|
681
|
+
inputs: [
|
|
682
|
+
{
|
|
683
|
+
indexed: true,
|
|
684
|
+
internalType: "address",
|
|
685
|
+
name: "previousOwner",
|
|
686
|
+
type: "address"
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
indexed: true,
|
|
690
|
+
internalType: "address",
|
|
691
|
+
name: "newOwner",
|
|
692
|
+
type: "address"
|
|
693
|
+
}
|
|
694
|
+
],
|
|
695
|
+
name: "OwnershipTransferStarted",
|
|
696
|
+
type: "event"
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
anonymous: false,
|
|
700
|
+
inputs: [
|
|
701
|
+
{
|
|
702
|
+
indexed: true,
|
|
703
|
+
internalType: "address",
|
|
704
|
+
name: "previousOwner",
|
|
705
|
+
type: "address"
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
indexed: true,
|
|
709
|
+
internalType: "address",
|
|
710
|
+
name: "newOwner",
|
|
711
|
+
type: "address"
|
|
712
|
+
}
|
|
713
|
+
],
|
|
714
|
+
name: "OwnershipTransferred",
|
|
715
|
+
type: "event"
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
anonymous: false,
|
|
719
|
+
inputs: [
|
|
720
|
+
{
|
|
721
|
+
indexed: true,
|
|
722
|
+
internalType: "address",
|
|
723
|
+
name: "oldRoute",
|
|
724
|
+
type: "address"
|
|
725
|
+
},
|
|
726
|
+
{
|
|
727
|
+
indexed: true,
|
|
728
|
+
internalType: "address",
|
|
729
|
+
name: "newRoute",
|
|
730
|
+
type: "address"
|
|
731
|
+
}
|
|
732
|
+
],
|
|
733
|
+
name: "SwapRouteUpdated",
|
|
734
|
+
type: "event"
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
anonymous: false,
|
|
738
|
+
inputs: [
|
|
739
|
+
{
|
|
740
|
+
indexed: true,
|
|
741
|
+
internalType: "address",
|
|
742
|
+
name: "token",
|
|
743
|
+
type: "address"
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
indexed: true,
|
|
747
|
+
internalType: "address",
|
|
748
|
+
name: "to",
|
|
749
|
+
type: "address"
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
indexed: false,
|
|
753
|
+
internalType: "uint256",
|
|
754
|
+
name: "amount",
|
|
755
|
+
type: "uint256"
|
|
756
|
+
}
|
|
757
|
+
],
|
|
758
|
+
name: "TokensRescued",
|
|
759
|
+
type: "event"
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
anonymous: false,
|
|
763
|
+
inputs: [
|
|
764
|
+
{
|
|
765
|
+
indexed: true,
|
|
766
|
+
internalType: "uint256",
|
|
767
|
+
name: "batchId",
|
|
768
|
+
type: "uint256"
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
indexed: true,
|
|
772
|
+
internalType: "address",
|
|
773
|
+
name: "user",
|
|
774
|
+
type: "address"
|
|
775
|
+
}
|
|
776
|
+
],
|
|
777
|
+
name: "Withdrawn",
|
|
778
|
+
type: "event"
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
inputs: [],
|
|
782
|
+
name: "acceptOwnership",
|
|
783
|
+
outputs: [],
|
|
784
|
+
stateMutability: "nonpayable",
|
|
785
|
+
type: "function"
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
inputs: [],
|
|
789
|
+
name: "batchExpiry",
|
|
790
|
+
outputs: [
|
|
791
|
+
{
|
|
792
|
+
internalType: "uint256",
|
|
793
|
+
name: "",
|
|
794
|
+
type: "uint256"
|
|
795
|
+
}
|
|
796
|
+
],
|
|
797
|
+
stateMutability: "view",
|
|
798
|
+
type: "function"
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
inputs: [
|
|
802
|
+
{
|
|
803
|
+
internalType: "uint256",
|
|
804
|
+
name: "batchId",
|
|
805
|
+
type: "uint256"
|
|
806
|
+
}
|
|
807
|
+
],
|
|
808
|
+
name: "claim",
|
|
809
|
+
outputs: [],
|
|
810
|
+
stateMutability: "nonpayable",
|
|
811
|
+
type: "function"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
inputs: [
|
|
815
|
+
{
|
|
816
|
+
internalType: "uint256",
|
|
817
|
+
name: "batchId",
|
|
818
|
+
type: "uint256"
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
internalType: "address",
|
|
822
|
+
name: "user",
|
|
823
|
+
type: "address"
|
|
824
|
+
}
|
|
825
|
+
],
|
|
826
|
+
name: "claim",
|
|
827
|
+
outputs: [],
|
|
828
|
+
stateMutability: "nonpayable",
|
|
829
|
+
type: "function"
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
inputs: [],
|
|
833
|
+
name: "confidentialProtocolId",
|
|
834
|
+
outputs: [
|
|
835
|
+
{
|
|
836
|
+
internalType: "uint256",
|
|
837
|
+
name: "",
|
|
838
|
+
type: "uint256"
|
|
839
|
+
}
|
|
840
|
+
],
|
|
841
|
+
stateMutability: "view",
|
|
842
|
+
type: "function"
|
|
843
|
+
},
|
|
844
|
+
{
|
|
845
|
+
inputs: [],
|
|
846
|
+
name: "coordinator",
|
|
847
|
+
outputs: [
|
|
848
|
+
{
|
|
849
|
+
internalType: "contract DeploymentCoordinator",
|
|
850
|
+
name: "",
|
|
851
|
+
type: "address"
|
|
852
|
+
}
|
|
853
|
+
],
|
|
854
|
+
stateMutability: "view",
|
|
855
|
+
type: "function"
|
|
856
|
+
},
|
|
857
|
+
{
|
|
858
|
+
inputs: [],
|
|
859
|
+
name: "currentBatchId",
|
|
860
|
+
outputs: [
|
|
861
|
+
{
|
|
862
|
+
internalType: "uint256",
|
|
863
|
+
name: "",
|
|
864
|
+
type: "uint256"
|
|
865
|
+
}
|
|
866
|
+
],
|
|
867
|
+
stateMutability: "view",
|
|
868
|
+
type: "function"
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
inputs: [
|
|
872
|
+
{
|
|
873
|
+
internalType: "uint256",
|
|
874
|
+
name: "batchId",
|
|
875
|
+
type: "uint256"
|
|
876
|
+
}
|
|
877
|
+
],
|
|
878
|
+
name: "dispatchBatch",
|
|
879
|
+
outputs: [],
|
|
880
|
+
stateMutability: "nonpayable",
|
|
881
|
+
type: "function"
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
inputs: [
|
|
885
|
+
{
|
|
886
|
+
internalType: "uint256",
|
|
887
|
+
name: "batchId",
|
|
888
|
+
type: "uint256"
|
|
889
|
+
}
|
|
890
|
+
],
|
|
891
|
+
name: "finalizeBatch",
|
|
892
|
+
outputs: [],
|
|
893
|
+
stateMutability: "nonpayable",
|
|
894
|
+
type: "function"
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
inputs: [],
|
|
898
|
+
name: "fromToken",
|
|
899
|
+
outputs: [
|
|
900
|
+
{
|
|
901
|
+
internalType: "contract RegulatedERC7984Upgradeable",
|
|
902
|
+
name: "",
|
|
903
|
+
type: "address"
|
|
904
|
+
}
|
|
905
|
+
],
|
|
906
|
+
stateMutability: "view",
|
|
907
|
+
type: "function"
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
inputs: [],
|
|
911
|
+
name: "fromWrapper",
|
|
912
|
+
outputs: [
|
|
913
|
+
{
|
|
914
|
+
internalType: "contract ConfidentialWrapper",
|
|
915
|
+
name: "",
|
|
916
|
+
type: "address"
|
|
917
|
+
}
|
|
918
|
+
],
|
|
919
|
+
stateMutability: "view",
|
|
920
|
+
type: "function"
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
inputs: [
|
|
924
|
+
{
|
|
925
|
+
internalType: "uint256",
|
|
926
|
+
name: "batchId",
|
|
927
|
+
type: "uint256"
|
|
928
|
+
}
|
|
929
|
+
],
|
|
930
|
+
name: "getBatch",
|
|
931
|
+
outputs: [
|
|
932
|
+
{
|
|
933
|
+
internalType: "enum Batcher.BatchStatus",
|
|
934
|
+
name: "status",
|
|
935
|
+
type: "uint8"
|
|
936
|
+
},
|
|
937
|
+
{
|
|
938
|
+
internalType: "uint256",
|
|
939
|
+
name: "createdAt",
|
|
940
|
+
type: "uint256"
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
internalType: "uint256",
|
|
944
|
+
name: "dispatchedAt",
|
|
945
|
+
type: "uint256"
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
internalType: "uint256",
|
|
949
|
+
name: "totalDepositedPlaintext",
|
|
950
|
+
type: "uint256"
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
internalType: "uint64",
|
|
954
|
+
name: "exchangeRate",
|
|
955
|
+
type: "uint64"
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
internalType: "uint64",
|
|
959
|
+
name: "mantissa",
|
|
960
|
+
type: "uint64"
|
|
961
|
+
},
|
|
962
|
+
{
|
|
963
|
+
internalType: "uint256",
|
|
964
|
+
name: "depositCount",
|
|
965
|
+
type: "uint256"
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
internalType: "uint256",
|
|
969
|
+
name: "unwrapRequestId",
|
|
970
|
+
type: "uint256"
|
|
971
|
+
}
|
|
972
|
+
],
|
|
973
|
+
stateMutability: "view",
|
|
974
|
+
type: "function"
|
|
975
|
+
},
|
|
976
|
+
{
|
|
977
|
+
inputs: [
|
|
978
|
+
{
|
|
979
|
+
internalType: "uint256",
|
|
980
|
+
name: "batchId",
|
|
981
|
+
type: "uint256"
|
|
982
|
+
}
|
|
983
|
+
],
|
|
984
|
+
name: "getDepositorCount",
|
|
985
|
+
outputs: [
|
|
986
|
+
{
|
|
987
|
+
internalType: "uint256",
|
|
988
|
+
name: "",
|
|
989
|
+
type: "uint256"
|
|
990
|
+
}
|
|
991
|
+
],
|
|
992
|
+
stateMutability: "view",
|
|
993
|
+
type: "function"
|
|
994
|
+
},
|
|
995
|
+
{
|
|
996
|
+
inputs: [
|
|
997
|
+
{
|
|
998
|
+
internalType: "uint256",
|
|
999
|
+
name: "batchId",
|
|
1000
|
+
type: "uint256"
|
|
1001
|
+
}
|
|
1002
|
+
],
|
|
1003
|
+
name: "getDepositors",
|
|
1004
|
+
outputs: [
|
|
1005
|
+
{
|
|
1006
|
+
internalType: "address[]",
|
|
1007
|
+
name: "",
|
|
1008
|
+
type: "address[]"
|
|
1009
|
+
}
|
|
1010
|
+
],
|
|
1011
|
+
stateMutability: "view",
|
|
1012
|
+
type: "function"
|
|
1013
|
+
},
|
|
1014
|
+
{
|
|
1015
|
+
inputs: [
|
|
1016
|
+
{
|
|
1017
|
+
internalType: "uint256",
|
|
1018
|
+
name: "batchId",
|
|
1019
|
+
type: "uint256"
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
internalType: "address",
|
|
1023
|
+
name: "user",
|
|
1024
|
+
type: "address"
|
|
1025
|
+
}
|
|
1026
|
+
],
|
|
1027
|
+
name: "hasDeposit",
|
|
1028
|
+
outputs: [
|
|
1029
|
+
{
|
|
1030
|
+
internalType: "bool",
|
|
1031
|
+
name: "",
|
|
1032
|
+
type: "bool"
|
|
1033
|
+
}
|
|
1034
|
+
],
|
|
1035
|
+
stateMutability: "view",
|
|
1036
|
+
type: "function"
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
inputs: [
|
|
1040
|
+
{
|
|
1041
|
+
internalType: "uint256",
|
|
1042
|
+
name: "batchId",
|
|
1043
|
+
type: "uint256"
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
internalType: "address",
|
|
1047
|
+
name: "user",
|
|
1048
|
+
type: "address"
|
|
1049
|
+
}
|
|
1050
|
+
],
|
|
1051
|
+
name: "isClaimable",
|
|
1052
|
+
outputs: [
|
|
1053
|
+
{
|
|
1054
|
+
internalType: "bool",
|
|
1055
|
+
name: "",
|
|
1056
|
+
type: "bool"
|
|
1057
|
+
}
|
|
1058
|
+
],
|
|
1059
|
+
stateMutability: "view",
|
|
1060
|
+
type: "function"
|
|
1061
|
+
},
|
|
1062
|
+
{
|
|
1063
|
+
inputs: [
|
|
1064
|
+
{
|
|
1065
|
+
internalType: "uint256",
|
|
1066
|
+
name: "batchId",
|
|
1067
|
+
type: "uint256"
|
|
1068
|
+
},
|
|
1069
|
+
{
|
|
1070
|
+
internalType: "address",
|
|
1071
|
+
name: "user",
|
|
1072
|
+
type: "address"
|
|
1073
|
+
}
|
|
1074
|
+
],
|
|
1075
|
+
name: "isClaimed",
|
|
1076
|
+
outputs: [
|
|
1077
|
+
{
|
|
1078
|
+
internalType: "bool",
|
|
1079
|
+
name: "",
|
|
1080
|
+
type: "bool"
|
|
1081
|
+
}
|
|
1082
|
+
],
|
|
1083
|
+
stateMutability: "view",
|
|
1084
|
+
type: "function"
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
inputs: [],
|
|
1088
|
+
name: "minBatchDuration",
|
|
1089
|
+
outputs: [
|
|
1090
|
+
{
|
|
1091
|
+
internalType: "uint256",
|
|
1092
|
+
name: "",
|
|
1093
|
+
type: "uint256"
|
|
1094
|
+
}
|
|
1095
|
+
],
|
|
1096
|
+
stateMutability: "view",
|
|
1097
|
+
type: "function"
|
|
1098
|
+
},
|
|
1099
|
+
{
|
|
1100
|
+
inputs: [],
|
|
1101
|
+
name: "minDepositors",
|
|
1102
|
+
outputs: [
|
|
1103
|
+
{
|
|
1104
|
+
internalType: "uint256",
|
|
1105
|
+
name: "",
|
|
1106
|
+
type: "uint256"
|
|
1107
|
+
}
|
|
1108
|
+
],
|
|
1109
|
+
stateMutability: "view",
|
|
1110
|
+
type: "function"
|
|
1111
|
+
},
|
|
1112
|
+
{
|
|
1113
|
+
inputs: [
|
|
1114
|
+
{
|
|
1115
|
+
internalType: "address",
|
|
1116
|
+
name: "",
|
|
1117
|
+
type: "address"
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
internalType: "address",
|
|
1121
|
+
name: "from",
|
|
1122
|
+
type: "address"
|
|
1123
|
+
},
|
|
1124
|
+
{
|
|
1125
|
+
internalType: "euint64",
|
|
1126
|
+
name: "amount",
|
|
1127
|
+
type: "bytes32"
|
|
1128
|
+
},
|
|
1129
|
+
{
|
|
1130
|
+
internalType: "bytes",
|
|
1131
|
+
name: "",
|
|
1132
|
+
type: "bytes"
|
|
1133
|
+
}
|
|
1134
|
+
],
|
|
1135
|
+
name: "onConfidentialTransferReceived",
|
|
1136
|
+
outputs: [
|
|
1137
|
+
{
|
|
1138
|
+
internalType: "ebool",
|
|
1139
|
+
name: "",
|
|
1140
|
+
type: "bytes32"
|
|
1141
|
+
}
|
|
1142
|
+
],
|
|
1143
|
+
stateMutability: "nonpayable",
|
|
1144
|
+
type: "function"
|
|
1145
|
+
},
|
|
1146
|
+
{
|
|
1147
|
+
inputs: [
|
|
1148
|
+
{
|
|
1149
|
+
internalType: "address",
|
|
1150
|
+
name: "",
|
|
1151
|
+
type: "address"
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
internalType: "uint256",
|
|
1155
|
+
name: "amount",
|
|
1156
|
+
type: "uint256"
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
internalType: "euint64",
|
|
1160
|
+
name: "burntAmount",
|
|
1161
|
+
type: "bytes32"
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
internalType: "address",
|
|
1165
|
+
name: "",
|
|
1166
|
+
type: "address"
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
internalType: "bytes",
|
|
1170
|
+
name: "data",
|
|
1171
|
+
type: "bytes"
|
|
1172
|
+
}
|
|
1173
|
+
],
|
|
1174
|
+
name: "onUnwrapFinalizedReceived",
|
|
1175
|
+
outputs: [
|
|
1176
|
+
{
|
|
1177
|
+
internalType: "bool",
|
|
1178
|
+
name: "",
|
|
1179
|
+
type: "bool"
|
|
1180
|
+
}
|
|
1181
|
+
],
|
|
1182
|
+
stateMutability: "nonpayable",
|
|
1183
|
+
type: "function"
|
|
1184
|
+
},
|
|
1185
|
+
{
|
|
1186
|
+
inputs: [],
|
|
1187
|
+
name: "owner",
|
|
1188
|
+
outputs: [
|
|
1189
|
+
{
|
|
1190
|
+
internalType: "address",
|
|
1191
|
+
name: "",
|
|
1192
|
+
type: "address"
|
|
1193
|
+
}
|
|
1194
|
+
],
|
|
1195
|
+
stateMutability: "view",
|
|
1196
|
+
type: "function"
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
inputs: [],
|
|
1200
|
+
name: "pendingOwner",
|
|
1201
|
+
outputs: [
|
|
1202
|
+
{
|
|
1203
|
+
internalType: "address",
|
|
1204
|
+
name: "",
|
|
1205
|
+
type: "address"
|
|
1206
|
+
}
|
|
1207
|
+
],
|
|
1208
|
+
stateMutability: "view",
|
|
1209
|
+
type: "function"
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
inputs: [
|
|
1213
|
+
{
|
|
1214
|
+
internalType: "uint256",
|
|
1215
|
+
name: "batchId",
|
|
1216
|
+
type: "uint256"
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
internalType: "uint256",
|
|
1220
|
+
name: "startIndex",
|
|
1221
|
+
type: "uint256"
|
|
1222
|
+
},
|
|
1223
|
+
{
|
|
1224
|
+
internalType: "uint256",
|
|
1225
|
+
name: "count",
|
|
1226
|
+
type: "uint256"
|
|
1227
|
+
}
|
|
1228
|
+
],
|
|
1229
|
+
name: "pushClaims",
|
|
1230
|
+
outputs: [],
|
|
1231
|
+
stateMutability: "nonpayable",
|
|
1232
|
+
type: "function"
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
inputs: [
|
|
1236
|
+
{
|
|
1237
|
+
internalType: "uint256",
|
|
1238
|
+
name: "batchId",
|
|
1239
|
+
type: "uint256"
|
|
1240
|
+
}
|
|
1241
|
+
],
|
|
1242
|
+
name: "recoverFailedBatch",
|
|
1243
|
+
outputs: [],
|
|
1244
|
+
stateMutability: "nonpayable",
|
|
1245
|
+
type: "function"
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
inputs: [],
|
|
1249
|
+
name: "renounceOwnership",
|
|
1250
|
+
outputs: [],
|
|
1251
|
+
stateMutability: "nonpayable",
|
|
1252
|
+
type: "function"
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
inputs: [
|
|
1256
|
+
{
|
|
1257
|
+
internalType: "address",
|
|
1258
|
+
name: "token",
|
|
1259
|
+
type: "address"
|
|
1260
|
+
},
|
|
1261
|
+
{
|
|
1262
|
+
internalType: "address",
|
|
1263
|
+
name: "to",
|
|
1264
|
+
type: "address"
|
|
1265
|
+
},
|
|
1266
|
+
{
|
|
1267
|
+
internalType: "uint256",
|
|
1268
|
+
name: "amount",
|
|
1269
|
+
type: "uint256"
|
|
1270
|
+
}
|
|
1271
|
+
],
|
|
1272
|
+
name: "rescueTokens",
|
|
1273
|
+
outputs: [],
|
|
1274
|
+
stateMutability: "nonpayable",
|
|
1275
|
+
type: "function"
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
inputs: [
|
|
1279
|
+
{
|
|
1280
|
+
internalType: "address",
|
|
1281
|
+
name: "operator",
|
|
1282
|
+
type: "address"
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
internalType: "uint48",
|
|
1286
|
+
name: "until",
|
|
1287
|
+
type: "uint48"
|
|
1288
|
+
}
|
|
1289
|
+
],
|
|
1290
|
+
name: "setFinalizeUnwrapOperator",
|
|
1291
|
+
outputs: [],
|
|
1292
|
+
stateMutability: "nonpayable",
|
|
1293
|
+
type: "function"
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
inputs: [
|
|
1297
|
+
{
|
|
1298
|
+
internalType: "contract ISwapRoute",
|
|
1299
|
+
name: "newRoute",
|
|
1300
|
+
type: "address"
|
|
1301
|
+
}
|
|
1302
|
+
],
|
|
1303
|
+
name: "setSwapRoute",
|
|
1304
|
+
outputs: [],
|
|
1305
|
+
stateMutability: "nonpayable",
|
|
1306
|
+
type: "function"
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
inputs: [],
|
|
1310
|
+
name: "swapRoute",
|
|
1311
|
+
outputs: [
|
|
1312
|
+
{
|
|
1313
|
+
internalType: "contract ISwapRoute",
|
|
1314
|
+
name: "",
|
|
1315
|
+
type: "address"
|
|
1316
|
+
}
|
|
1317
|
+
],
|
|
1318
|
+
stateMutability: "view",
|
|
1319
|
+
type: "function"
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
inputs: [],
|
|
1323
|
+
name: "toToken",
|
|
1324
|
+
outputs: [
|
|
1325
|
+
{
|
|
1326
|
+
internalType: "contract RegulatedERC7984Upgradeable",
|
|
1327
|
+
name: "",
|
|
1328
|
+
type: "address"
|
|
1329
|
+
}
|
|
1330
|
+
],
|
|
1331
|
+
stateMutability: "view",
|
|
1332
|
+
type: "function"
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
inputs: [],
|
|
1336
|
+
name: "toWrapper",
|
|
1337
|
+
outputs: [
|
|
1338
|
+
{
|
|
1339
|
+
internalType: "contract ConfidentialWrapper",
|
|
1340
|
+
name: "",
|
|
1341
|
+
type: "address"
|
|
1342
|
+
}
|
|
1343
|
+
],
|
|
1344
|
+
stateMutability: "view",
|
|
1345
|
+
type: "function"
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
inputs: [
|
|
1349
|
+
{
|
|
1350
|
+
internalType: "address",
|
|
1351
|
+
name: "newOwner",
|
|
1352
|
+
type: "address"
|
|
1353
|
+
}
|
|
1354
|
+
],
|
|
1355
|
+
name: "transferOwnership",
|
|
1356
|
+
outputs: [],
|
|
1357
|
+
stateMutability: "nonpayable",
|
|
1358
|
+
type: "function"
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
inputs: [
|
|
1362
|
+
{
|
|
1363
|
+
internalType: "uint256",
|
|
1364
|
+
name: "batchId",
|
|
1365
|
+
type: "uint256"
|
|
1366
|
+
}
|
|
1367
|
+
],
|
|
1368
|
+
name: "withdraw",
|
|
1369
|
+
outputs: [],
|
|
1370
|
+
stateMutability: "nonpayable",
|
|
1371
|
+
type: "function"
|
|
1372
|
+
},
|
|
1373
|
+
{
|
|
1374
|
+
stateMutability: "payable",
|
|
1375
|
+
type: "receive"
|
|
1376
|
+
}
|
|
1377
|
+
];
|
|
1378
|
+
|
|
1379
|
+
// src/events/onchain-events.ts
|
|
1380
|
+
var Topics = {
|
|
1381
|
+
/** `ConfidentialTransfer(address indexed from, address indexed to, bytes32 indexed amount)` */
|
|
1382
|
+
ConfidentialTransfer: "0x67500e8d0ed826d2194f514dd0d8124f35648ab6e3fb5e6ed867134cffe661e9",
|
|
1383
|
+
/** `Wrapped(uint64 mintAmount, uint256 amountIn, uint256 feeAmount, address indexed to_, uint256 indexed mintTxId)` */
|
|
1384
|
+
Wrapped: "0x1f7907f4d84043abe0fb7c74e8865ee5fe93fe4f691c54a7b8fa9d6fb17c7cba",
|
|
1385
|
+
/** `UnwrapRequested(address indexed receiver, bytes32 amount)` */
|
|
1386
|
+
UnwrapRequested: "0x77d02d353c5629272875d11f1b34ec4c65d7430b075575b78cd2502034c469ee",
|
|
1387
|
+
/** `UnwrappedFinalized(bytes32 indexed burntAmountHandle, ...)` */
|
|
1388
|
+
UnwrappedFinalized: "0xc64e7c81b18b674fc5b037d8a0041bfe3332d86c780a4688f404ee01fbabb152",
|
|
1389
|
+
/** `UnwrappedStarted(bool returnVal, uint256 indexed requestId, ...)` */
|
|
1390
|
+
UnwrappedStarted: "0x3838891d4843c6d7f9f494570b6fd8843f4e3c3ddb817c1411760bd31b819806"
|
|
1391
|
+
};
|
|
1392
|
+
function topicToAddress(topic) {
|
|
1393
|
+
return "0x" + topic.slice(-40);
|
|
1394
|
+
}
|
|
1395
|
+
function topicToBigInt(topic) {
|
|
1396
|
+
return BigInt(topic);
|
|
1397
|
+
}
|
|
1398
|
+
function topicToBytes32(topic) {
|
|
1399
|
+
return topic;
|
|
1400
|
+
}
|
|
1401
|
+
function wordAt(data, index) {
|
|
1402
|
+
const start = 2 + index * 64;
|
|
1403
|
+
const word = data.slice(start, start + 64);
|
|
1404
|
+
return word.length === 64 ? word : word.padEnd(64, "0");
|
|
1405
|
+
}
|
|
1406
|
+
function wordToAddress(data, index) {
|
|
1407
|
+
return "0x" + wordAt(data, index).slice(-40);
|
|
1408
|
+
}
|
|
1409
|
+
function wordToBigInt(data, index) {
|
|
1410
|
+
return BigInt("0x" + wordAt(data, index));
|
|
1411
|
+
}
|
|
1412
|
+
function wordToBool(data, index) {
|
|
1413
|
+
return BigInt("0x" + wordAt(data, index)) !== 0n;
|
|
1414
|
+
}
|
|
1415
|
+
function wordToBytes32(data, index) {
|
|
1416
|
+
return `0x${wordAt(data, index)}`;
|
|
1417
|
+
}
|
|
1418
|
+
function decodeConfidentialTransfer(log) {
|
|
1419
|
+
if (log.topics[0] !== Topics.ConfidentialTransfer) return null;
|
|
1420
|
+
if (log.topics.length < 4) return null;
|
|
1421
|
+
return {
|
|
1422
|
+
eventName: "ConfidentialTransfer",
|
|
1423
|
+
from: topicToAddress(log.topics[1]),
|
|
1424
|
+
to: topicToAddress(log.topics[2]),
|
|
1425
|
+
encryptedAmountHandle: topicToBytes32(log.topics[3])
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
function decodeWrapped(log) {
|
|
1429
|
+
if (log.topics[0] !== Topics.Wrapped) return null;
|
|
1430
|
+
if (log.topics.length < 3) return null;
|
|
1431
|
+
return {
|
|
1432
|
+
eventName: "Wrapped",
|
|
1433
|
+
to: topicToAddress(log.topics[1]),
|
|
1434
|
+
mintTxId: topicToBigInt(log.topics[2]),
|
|
1435
|
+
mintAmount: wordToBigInt(log.data, 0),
|
|
1436
|
+
amountIn: wordToBigInt(log.data, 1),
|
|
1437
|
+
feeAmount: wordToBigInt(log.data, 2)
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
function decodeUnwrapRequested(log) {
|
|
1441
|
+
if (log.topics[0] !== Topics.UnwrapRequested) return null;
|
|
1442
|
+
if (log.topics.length < 2) return null;
|
|
1443
|
+
return {
|
|
1444
|
+
eventName: "UnwrapRequested",
|
|
1445
|
+
receiver: topicToAddress(log.topics[1]),
|
|
1446
|
+
encryptedAmount: wordToBytes32(log.data, 0)
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
function decodeUnwrappedFinalized(log) {
|
|
1450
|
+
if (log.topics[0] !== Topics.UnwrappedFinalized) return null;
|
|
1451
|
+
if (log.topics.length < 3) return null;
|
|
1452
|
+
return {
|
|
1453
|
+
eventName: "UnwrappedFinalized",
|
|
1454
|
+
burntAmountHandle: topicToBytes32(log.topics[1]),
|
|
1455
|
+
nextTxId: topicToBigInt(log.topics[2]),
|
|
1456
|
+
finalizeSuccess: wordToBool(log.data, 0),
|
|
1457
|
+
feeTransferSuccess: wordToBool(log.data, 1),
|
|
1458
|
+
burnAmount: wordToBigInt(log.data, 2),
|
|
1459
|
+
unwrapAmount: wordToBigInt(log.data, 3),
|
|
1460
|
+
feeAmount: wordToBigInt(log.data, 4)
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function decodeUnwrappedStarted(log) {
|
|
1464
|
+
if (log.topics[0] !== Topics.UnwrappedStarted) return null;
|
|
1465
|
+
if (log.topics.length < 4) return null;
|
|
1466
|
+
return {
|
|
1467
|
+
eventName: "UnwrappedStarted",
|
|
1468
|
+
requestId: topicToBigInt(log.topics[1]),
|
|
1469
|
+
txId: topicToBigInt(log.topics[2]),
|
|
1470
|
+
to: topicToAddress(log.topics[3]),
|
|
1471
|
+
returnVal: wordToBool(log.data, 0),
|
|
1472
|
+
refund: wordToAddress(log.data, 1),
|
|
1473
|
+
requestedAmount: wordToBytes32(log.data, 2),
|
|
1474
|
+
burnAmount: wordToBytes32(log.data, 3)
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
function decodeOnChainEvent(log) {
|
|
1478
|
+
return decodeConfidentialTransfer(log) ?? decodeWrapped(log) ?? decodeUnwrapRequested(log) ?? decodeUnwrappedFinalized(log) ?? decodeUnwrappedStarted(log);
|
|
1479
|
+
}
|
|
1480
|
+
function decodeOnChainEvents(logs) {
|
|
1481
|
+
const events = [];
|
|
1482
|
+
for (const log of logs) {
|
|
1483
|
+
const event = decodeOnChainEvent(log);
|
|
1484
|
+
if (event) events.push(event);
|
|
1485
|
+
}
|
|
1486
|
+
return events;
|
|
1487
|
+
}
|
|
1488
|
+
function findUnwrapRequested(logs) {
|
|
1489
|
+
for (const log of logs) {
|
|
1490
|
+
const event = decodeUnwrapRequested(log);
|
|
1491
|
+
if (event) return event;
|
|
1492
|
+
}
|
|
1493
|
+
return null;
|
|
1494
|
+
}
|
|
1495
|
+
function findWrapped(logs) {
|
|
1496
|
+
for (const log of logs) {
|
|
1497
|
+
const event = decodeWrapped(log);
|
|
1498
|
+
if (event) return event;
|
|
1499
|
+
}
|
|
1500
|
+
return null;
|
|
1501
|
+
}
|
|
1502
|
+
var TOKEN_TOPICS = [
|
|
1503
|
+
Topics.ConfidentialTransfer,
|
|
1504
|
+
Topics.Wrapped,
|
|
1505
|
+
Topics.UnwrapRequested,
|
|
1506
|
+
Topics.UnwrappedFinalized,
|
|
1507
|
+
Topics.UnwrappedStarted
|
|
1508
|
+
];
|
|
1509
|
+
|
|
1510
|
+
// src/token/memory-storage.ts
|
|
1511
|
+
var MemoryStorage = class {
|
|
1512
|
+
#map = /* @__PURE__ */ new Map();
|
|
1513
|
+
async get(key) {
|
|
1514
|
+
return this.#map.get(key) ?? null;
|
|
1515
|
+
}
|
|
1516
|
+
async set(key, value) {
|
|
1517
|
+
this.#map.set(key, value);
|
|
1518
|
+
}
|
|
1519
|
+
async delete(key) {
|
|
1520
|
+
this.#map.delete(key);
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
var memoryStorage = new MemoryStorage();
|
|
1524
|
+
|
|
1525
|
+
// src/events/sdk-events.ts
|
|
1526
|
+
var ZamaSDKEvents = {
|
|
1527
|
+
// Credentials lifecycle
|
|
1528
|
+
CredentialsLoading: "credentials:loading",
|
|
1529
|
+
CredentialsCached: "credentials:cached",
|
|
1530
|
+
CredentialsExpired: "credentials:expired",
|
|
1531
|
+
CredentialsCreating: "credentials:creating",
|
|
1532
|
+
CredentialsCreated: "credentials:created",
|
|
1533
|
+
CredentialsRevoked: "credentials:revoked",
|
|
1534
|
+
CredentialsAllowed: "credentials:allowed",
|
|
1535
|
+
// FHE operations
|
|
1536
|
+
EncryptStart: "encrypt:start",
|
|
1537
|
+
EncryptEnd: "encrypt:end",
|
|
1538
|
+
EncryptError: "encrypt:error",
|
|
1539
|
+
DecryptStart: "decrypt:start",
|
|
1540
|
+
DecryptEnd: "decrypt:end",
|
|
1541
|
+
DecryptError: "decrypt:error",
|
|
1542
|
+
// Write operations
|
|
1543
|
+
TransactionError: "transaction:error",
|
|
1544
|
+
ShieldSubmitted: "shield:submitted",
|
|
1545
|
+
TransferSubmitted: "transfer:submitted",
|
|
1546
|
+
TransferFromSubmitted: "transferFrom:submitted",
|
|
1547
|
+
ApproveSubmitted: "approve:submitted",
|
|
1548
|
+
ApproveUnderlyingSubmitted: "approveUnderlying:submitted",
|
|
1549
|
+
UnwrapSubmitted: "unwrap:submitted",
|
|
1550
|
+
FinalizeUnwrapSubmitted: "finalizeUnwrap:submitted",
|
|
1551
|
+
// Unshield orchestration
|
|
1552
|
+
UnshieldPhase1Submitted: "unshield:phase1_submitted",
|
|
1553
|
+
UnshieldPhase2Started: "unshield:phase2_started",
|
|
1554
|
+
UnshieldPhase2Submitted: "unshield:phase2_submitted"
|
|
1555
|
+
};
|
|
1556
|
+
|
|
1557
|
+
// src/token/credentials-manager.ts
|
|
1558
|
+
async function computeStoreKey(address, chainId) {
|
|
1559
|
+
const hash = await crypto.subtle.digest(
|
|
1560
|
+
"SHA-256",
|
|
1561
|
+
new TextEncoder().encode(`${address.toLowerCase()}:${chainId}`)
|
|
1562
|
+
);
|
|
1563
|
+
const hex = Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1564
|
+
return hex.slice(0, 32);
|
|
1565
|
+
}
|
|
1566
|
+
var CredentialsManager = class {
|
|
1567
|
+
#relayer;
|
|
1568
|
+
#signer;
|
|
1569
|
+
#storage;
|
|
1570
|
+
#sessionStorage;
|
|
1571
|
+
#durationDays;
|
|
1572
|
+
#onEvent;
|
|
1573
|
+
#createPromise = null;
|
|
1574
|
+
#createPromiseKey = null;
|
|
1575
|
+
constructor(config) {
|
|
1576
|
+
this.#relayer = config.relayer;
|
|
1577
|
+
this.#signer = config.signer;
|
|
1578
|
+
this.#storage = config.storage;
|
|
1579
|
+
this.#sessionStorage = config.sessionStorage;
|
|
1580
|
+
this.#durationDays = config.durationDays;
|
|
1581
|
+
this.#onEvent = config.onEvent ?? Boolean;
|
|
1582
|
+
const chrome2 = typeof globalThis !== "undefined" ? globalThis.chrome : void 0;
|
|
1583
|
+
if (chrome2?.runtime?.id && config.sessionStorage instanceof MemoryStorage) {
|
|
1584
|
+
console.warn(
|
|
1585
|
+
"[zama-sdk] Detected Chrome extension context with in-memory session storage. Session signatures will be lost on service worker restart and won't be shared across contexts. Consider using chromeSessionStorage instead. "
|
|
1586
|
+
);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
#emit(partial) {
|
|
1590
|
+
this.#onEvent({ ...partial, timestamp: Date.now() });
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Authorize FHE credentials for one or more contract addresses.
|
|
1594
|
+
* Returns cached credentials if still valid and covering all addresses,
|
|
1595
|
+
* otherwise generates a fresh keypair and requests an EIP-712 signature.
|
|
1596
|
+
* The wallet signature is cached in session storage.
|
|
1597
|
+
*
|
|
1598
|
+
* @example
|
|
1599
|
+
* ```ts
|
|
1600
|
+
* const creds = await credentials.allow("0xTokenAddress");
|
|
1601
|
+
* const creds = await credentials.allow("0xTokenA", "0xTokenB");
|
|
1602
|
+
* ```
|
|
1603
|
+
*/
|
|
1604
|
+
async allow(...contractAddresses) {
|
|
1605
|
+
const storeKey = await this.#storeKey();
|
|
1606
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsLoading, contractAddresses });
|
|
1607
|
+
try {
|
|
1608
|
+
const stored = await this.#storage.get(storeKey);
|
|
1609
|
+
if (stored) {
|
|
1610
|
+
const encrypted = stored;
|
|
1611
|
+
this.#assertEncryptedCredentials(encrypted);
|
|
1612
|
+
if (this.#hasLegacySignature(encrypted)) {
|
|
1613
|
+
const creds = await this.#decryptCredentials(encrypted, encrypted.signature);
|
|
1614
|
+
if (this.#isValid(creds, contractAddresses)) {
|
|
1615
|
+
await this.#sessionStorage.set(storeKey, encrypted.signature);
|
|
1616
|
+
const migrated = await this.#encryptCredentials(creds);
|
|
1617
|
+
await this.#storage.set(storeKey, migrated).catch(() => {
|
|
1618
|
+
});
|
|
1619
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsCached, contractAddresses });
|
|
1620
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses });
|
|
1621
|
+
return creds;
|
|
1622
|
+
}
|
|
1623
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsExpired, contractAddresses });
|
|
1624
|
+
} else {
|
|
1625
|
+
const sessionSig = await this.#sessionStorage.get(storeKey);
|
|
1626
|
+
if (sessionSig) {
|
|
1627
|
+
const creds = await this.#decryptCredentials(encrypted, sessionSig);
|
|
1628
|
+
if (this.#isValid(creds, contractAddresses)) {
|
|
1629
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsCached, contractAddresses });
|
|
1630
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses });
|
|
1631
|
+
return creds;
|
|
1632
|
+
}
|
|
1633
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsExpired, contractAddresses });
|
|
1634
|
+
} else {
|
|
1635
|
+
if (this.#isValidWithoutDecrypt(encrypted, contractAddresses)) {
|
|
1636
|
+
const signature = await this.#sign(encrypted);
|
|
1637
|
+
await this.#sessionStorage.set(storeKey, signature);
|
|
1638
|
+
const creds = await this.#decryptCredentials(encrypted, signature);
|
|
1639
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsCached, contractAddresses });
|
|
1640
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses });
|
|
1641
|
+
return creds;
|
|
1642
|
+
}
|
|
1643
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsExpired, contractAddresses });
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
} catch {
|
|
1648
|
+
try {
|
|
1649
|
+
await this.#storage.delete(storeKey);
|
|
1650
|
+
} catch {
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
const key = contractAddresses.map((a) => a.toLowerCase()).sort().join(",");
|
|
1654
|
+
if (!this.#createPromise || this.#createPromiseKey !== key) {
|
|
1655
|
+
this.#createPromiseKey = key;
|
|
1656
|
+
this.#createPromise = this.create(contractAddresses).then((creds) => {
|
|
1657
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsAllowed, contractAddresses });
|
|
1658
|
+
return creds;
|
|
1659
|
+
}).finally(() => {
|
|
1660
|
+
this.#createPromise = null;
|
|
1661
|
+
this.#createPromiseKey = null;
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
return this.#createPromise;
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Check if stored credentials exist and are expired.
|
|
1668
|
+
* Returns `true` if credentials are stored but past their expiration time.
|
|
1669
|
+
* Returns `false` if no credentials are stored or if they are still valid.
|
|
1670
|
+
*
|
|
1671
|
+
* Use this to proactively detect expiration and show appropriate UI
|
|
1672
|
+
* (e.g. "Re-authorizing..." instead of a generic loading state).
|
|
1673
|
+
*
|
|
1674
|
+
* @param contractAddress - Optional contract address to check coverage for.
|
|
1675
|
+
* When provided, also returns `true` if credentials don't cover this address.
|
|
1676
|
+
*
|
|
1677
|
+
* @example
|
|
1678
|
+
* ```ts
|
|
1679
|
+
* if (await credentials.isExpired("0xTokenAddress")) {
|
|
1680
|
+
* showReauthorizingUI();
|
|
1681
|
+
* }
|
|
1682
|
+
* ```
|
|
1683
|
+
*/
|
|
1684
|
+
async isExpired(contractAddress) {
|
|
1685
|
+
const storeKey = await this.#storeKey();
|
|
1686
|
+
try {
|
|
1687
|
+
const stored = await this.#storage.get(storeKey);
|
|
1688
|
+
if (!stored) return false;
|
|
1689
|
+
const encrypted = stored;
|
|
1690
|
+
this.#assertEncryptedCredentials(encrypted);
|
|
1691
|
+
const requiredContracts = contractAddress ? [contractAddress] : [];
|
|
1692
|
+
return !this.#isValidWithoutDecrypt(encrypted, requiredContracts);
|
|
1693
|
+
} catch {
|
|
1694
|
+
return false;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Revoke the session signature for the connected wallet. Stored credentials
|
|
1699
|
+
* remain intact, but the next decrypt operation will require a fresh wallet
|
|
1700
|
+
* signature.
|
|
1701
|
+
*
|
|
1702
|
+
* @param contractAddresses - Optional addresses included in the revoked event
|
|
1703
|
+
* for observability. The session signature is always fully revoked since it
|
|
1704
|
+
* is atomic over all addresses.
|
|
1705
|
+
*
|
|
1706
|
+
* @example
|
|
1707
|
+
* ```ts
|
|
1708
|
+
* await credentials.revoke();
|
|
1709
|
+
* await credentials.revoke("0xTokenA", "0xTokenB");
|
|
1710
|
+
* ```
|
|
1711
|
+
*/
|
|
1712
|
+
async revoke(...contractAddresses) {
|
|
1713
|
+
const storeKey = await this.#storeKey();
|
|
1714
|
+
await this.#sessionStorage.delete(storeKey);
|
|
1715
|
+
this.#emit({
|
|
1716
|
+
type: ZamaSDKEvents.CredentialsRevoked,
|
|
1717
|
+
...contractAddresses.length > 0 && { contractAddresses }
|
|
1718
|
+
});
|
|
1719
|
+
}
|
|
1720
|
+
/**
|
|
1721
|
+
* Whether a session signature is currently cached for the connected wallet.
|
|
1722
|
+
*/
|
|
1723
|
+
async isAllowed() {
|
|
1724
|
+
const storeKey = await this.#storeKey();
|
|
1725
|
+
return await this.#sessionStorage.get(storeKey) !== null;
|
|
1726
|
+
}
|
|
1727
|
+
/**
|
|
1728
|
+
* Delete stored credentials for the connected wallet (best-effort).
|
|
1729
|
+
*
|
|
1730
|
+
* @example
|
|
1731
|
+
* ```ts
|
|
1732
|
+
* await credentials.clear();
|
|
1733
|
+
* ```
|
|
1734
|
+
*/
|
|
1735
|
+
async clear() {
|
|
1736
|
+
const storeKey = await this.#storeKey();
|
|
1737
|
+
await this.#sessionStorage.delete(storeKey);
|
|
1738
|
+
try {
|
|
1739
|
+
await this.#storage.delete(storeKey);
|
|
1740
|
+
} catch {
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
/** Returns a truncated SHA-256 hash of the address and chainId to avoid leaking it in storage. */
|
|
1744
|
+
async #storeKey() {
|
|
1745
|
+
const address = (await this.#signer.getAddress()).toLowerCase();
|
|
1746
|
+
const chainId = await this.#signer.getChainId();
|
|
1747
|
+
return computeStoreKey(address, chainId);
|
|
1748
|
+
}
|
|
1749
|
+
// ── Validation ──────────────────────────────────────────────
|
|
1750
|
+
#assertEncryptedCredentials(data) {
|
|
1751
|
+
assertObject(data, "Stored credentials");
|
|
1752
|
+
assertString(data.publicKey, "credentials.publicKey");
|
|
1753
|
+
assertArray(data.contractAddresses, "credentials.contractAddresses");
|
|
1754
|
+
assertObject(data.encryptedPrivateKey, "credentials.encryptedPrivateKey");
|
|
1755
|
+
assertString(data.encryptedPrivateKey.iv, "encryptedPrivateKey.iv");
|
|
1756
|
+
assertString(data.encryptedPrivateKey.ciphertext, "encryptedPrivateKey.ciphertext");
|
|
1757
|
+
}
|
|
1758
|
+
#hasLegacySignature(data) {
|
|
1759
|
+
return "signature" in data && typeof data.signature === "string";
|
|
1760
|
+
}
|
|
1761
|
+
#isValid(creds, requiredContracts) {
|
|
1762
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
1763
|
+
const expiresAt = creds.startTimestamp + creds.durationDays * 86400;
|
|
1764
|
+
if (nowSeconds >= expiresAt) return false;
|
|
1765
|
+
const signedSet = new Set(creds.contractAddresses.map((a) => a.toLowerCase()));
|
|
1766
|
+
return requiredContracts.every((addr) => signedSet.has(addr.toLowerCase()));
|
|
1767
|
+
}
|
|
1768
|
+
#isValidWithoutDecrypt(encrypted, requiredContracts) {
|
|
1769
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
1770
|
+
const expiresAt = encrypted.startTimestamp + encrypted.durationDays * 86400;
|
|
1771
|
+
if (nowSeconds >= expiresAt) return false;
|
|
1772
|
+
const signedSet = new Set(encrypted.contractAddresses.map((a) => a.toLowerCase()));
|
|
1773
|
+
return requiredContracts.every((addr) => signedSet.has(addr.toLowerCase()));
|
|
1774
|
+
}
|
|
1775
|
+
async #sign(encrypted) {
|
|
1776
|
+
const eip712 = await this.#relayer.createEIP712(
|
|
1777
|
+
encrypted.publicKey,
|
|
1778
|
+
encrypted.contractAddresses,
|
|
1779
|
+
encrypted.startTimestamp,
|
|
1780
|
+
encrypted.durationDays
|
|
1781
|
+
);
|
|
1782
|
+
return this.#signer.signTypedData(eip712);
|
|
1783
|
+
}
|
|
1784
|
+
// ── Credential generation ───────────────────────────────────
|
|
1785
|
+
/**
|
|
1786
|
+
* Generate a fresh FHE keypair, create an EIP-712 authorization, and
|
|
1787
|
+
* prompt the user to sign it. Persists the encrypted credentials to storage.
|
|
1788
|
+
*
|
|
1789
|
+
* @example
|
|
1790
|
+
* ```ts
|
|
1791
|
+
* const creds = await credentials.create(["0xTokenAddress"]);
|
|
1792
|
+
* ```
|
|
1793
|
+
*/
|
|
1794
|
+
async create(contractAddresses) {
|
|
1795
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsCreating, contractAddresses });
|
|
1796
|
+
try {
|
|
1797
|
+
const storeKey = await this.#storeKey();
|
|
1798
|
+
const keypair = await this.#relayer.generateKeypair();
|
|
1799
|
+
const startTimestamp = Math.floor(Date.now() / 1e3);
|
|
1800
|
+
const eip712 = await this.#relayer.createEIP712(
|
|
1801
|
+
keypair.publicKey,
|
|
1802
|
+
contractAddresses,
|
|
1803
|
+
startTimestamp,
|
|
1804
|
+
this.#durationDays
|
|
1805
|
+
);
|
|
1806
|
+
const signature = await this.#signer.signTypedData(eip712);
|
|
1807
|
+
await this.#sessionStorage.set(storeKey, signature);
|
|
1808
|
+
const creds = {
|
|
1809
|
+
publicKey: keypair.publicKey,
|
|
1810
|
+
privateKey: keypair.privateKey,
|
|
1811
|
+
signature,
|
|
1812
|
+
contractAddresses,
|
|
1813
|
+
startTimestamp,
|
|
1814
|
+
durationDays: this.#durationDays
|
|
1815
|
+
};
|
|
1816
|
+
try {
|
|
1817
|
+
const encrypted = await this.#encryptCredentials(creds);
|
|
1818
|
+
await this.#storage.set(storeKey, encrypted);
|
|
1819
|
+
} catch {
|
|
1820
|
+
}
|
|
1821
|
+
this.#emit({ type: ZamaSDKEvents.CredentialsCreated, contractAddresses });
|
|
1822
|
+
return creds;
|
|
1823
|
+
} catch (error) {
|
|
1824
|
+
const isRejected = error instanceof Error && "code" in error && error.code === 4001 || error instanceof Error && (error.message.includes("rejected") || error.message.includes("denied"));
|
|
1825
|
+
if (isRejected) {
|
|
1826
|
+
throw new SigningRejectedError("User rejected the decrypt authorization signature", {
|
|
1827
|
+
cause: error
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
throw new SigningFailedError("Failed to create decrypt credentials", {
|
|
1831
|
+
cause: error instanceof Error ? error : void 0
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
// ── AES-GCM encryption (ported from KeypairDB) ─────────────
|
|
1836
|
+
async #encryptCredentials(creds) {
|
|
1837
|
+
const address = (await this.#signer.getAddress()).toLowerCase();
|
|
1838
|
+
const encryptedPrivateKey = await this.#encrypt(creds.privateKey, creds.signature, address);
|
|
1839
|
+
const { privateKey: _, signature: _sig, ...rest } = creds;
|
|
1840
|
+
return { ...rest, encryptedPrivateKey };
|
|
1841
|
+
}
|
|
1842
|
+
async #decryptCredentials(encrypted, signature) {
|
|
1843
|
+
const address = (await this.#signer.getAddress()).toLowerCase();
|
|
1844
|
+
const privateKey = await this.#decrypt(encrypted.encryptedPrivateKey, signature, address);
|
|
1845
|
+
const { encryptedPrivateKey: _, ...rest } = encrypted;
|
|
1846
|
+
return { ...rest, privateKey, signature };
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Derives an AES-GCM encryption key from a wallet signature using PBKDF2.
|
|
1850
|
+
* The signature is a secret known only to the wallet holder, providing
|
|
1851
|
+
* meaningful encryption protection for the stored private key.
|
|
1852
|
+
*/
|
|
1853
|
+
async #deriveKey(signature, address) {
|
|
1854
|
+
const encoder = new TextEncoder();
|
|
1855
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
1856
|
+
"raw",
|
|
1857
|
+
encoder.encode(signature),
|
|
1858
|
+
"PBKDF2",
|
|
1859
|
+
false,
|
|
1860
|
+
["deriveKey"]
|
|
1861
|
+
);
|
|
1862
|
+
return crypto.subtle.deriveKey(
|
|
1863
|
+
{
|
|
1864
|
+
name: "PBKDF2",
|
|
1865
|
+
salt: encoder.encode(address),
|
|
1866
|
+
iterations: 6e5,
|
|
1867
|
+
hash: "SHA-256"
|
|
1868
|
+
},
|
|
1869
|
+
keyMaterial,
|
|
1870
|
+
{ name: "AES-GCM", length: 256 },
|
|
1871
|
+
false,
|
|
1872
|
+
["encrypt", "decrypt"]
|
|
1873
|
+
);
|
|
1874
|
+
}
|
|
1875
|
+
/** Encrypts a string using AES-GCM with a key derived from the wallet signature. */
|
|
1876
|
+
async #encrypt(plaintext, signature, address) {
|
|
1877
|
+
const key = await this.#deriveKey(signature, address);
|
|
1878
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
1879
|
+
const encoder = new TextEncoder();
|
|
1880
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
1881
|
+
{ name: "AES-GCM", iv },
|
|
1882
|
+
key,
|
|
1883
|
+
encoder.encode(plaintext)
|
|
1884
|
+
);
|
|
1885
|
+
return {
|
|
1886
|
+
iv: btoa(String.fromCharCode(...iv)),
|
|
1887
|
+
ciphertext: btoa(String.fromCharCode(...new Uint8Array(ciphertext)))
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
/** Decrypts AES-GCM encrypted data using a key derived from the wallet signature. */
|
|
1891
|
+
async #decrypt(encrypted, signature, address) {
|
|
1892
|
+
const key = await this.#deriveKey(signature, address);
|
|
1893
|
+
const iv = Uint8Array.from(atob(encrypted.iv), (c) => c.charCodeAt(0));
|
|
1894
|
+
const ciphertext = Uint8Array.from(atob(encrypted.ciphertext), (c) => c.charCodeAt(0));
|
|
1895
|
+
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
|
|
1896
|
+
return new TextDecoder().decode(plaintext);
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
|
|
1900
|
+
// src/token/balance-cache.ts
|
|
1901
|
+
var BALANCES_KEY = "zama:balances";
|
|
1902
|
+
function storageKey(tokenAddress, owner, handle) {
|
|
1903
|
+
return `zama:balance:${tokenAddress.toLowerCase()}:${owner.toLowerCase()}:${handle.toLowerCase()}`;
|
|
1904
|
+
}
|
|
1905
|
+
async function loadCachedBalance({
|
|
1906
|
+
storage,
|
|
1907
|
+
tokenAddress,
|
|
1908
|
+
owner,
|
|
1909
|
+
handle
|
|
1910
|
+
}) {
|
|
1911
|
+
try {
|
|
1912
|
+
const raw = await storage.get(storageKey(tokenAddress, owner, handle));
|
|
1913
|
+
return raw !== null ? BigInt(raw) : null;
|
|
1914
|
+
} catch {
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
async function saveCachedBalance(payload) {
|
|
1919
|
+
const { storage, tokenAddress, owner, handle, value } = payload;
|
|
1920
|
+
const key = storageKey(tokenAddress, owner, handle);
|
|
1921
|
+
try {
|
|
1922
|
+
await storage.set(key, value.toString());
|
|
1923
|
+
await trackKey(storage, key);
|
|
1924
|
+
} catch {
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
var trackKeyChains = /* @__PURE__ */ new WeakMap();
|
|
1928
|
+
async function trackKey(storage, key) {
|
|
1929
|
+
const prev = trackKeyChains.get(storage) ?? Promise.resolve();
|
|
1930
|
+
const next = prev.then(async () => {
|
|
1931
|
+
const raw = await storage.get(BALANCES_KEY);
|
|
1932
|
+
const keys = raw ? JSON.parse(raw) : [];
|
|
1933
|
+
if (!keys.includes(key)) {
|
|
1934
|
+
keys.push(key);
|
|
1935
|
+
await storage.set(BALANCES_KEY, JSON.stringify(keys));
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
trackKeyChains.set(
|
|
1939
|
+
storage,
|
|
1940
|
+
next.catch(() => {
|
|
1941
|
+
})
|
|
1942
|
+
);
|
|
1943
|
+
return next;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
// src/token/readonly-token.ts
|
|
1947
|
+
var ZERO_HANDLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
1948
|
+
var ReadonlyToken = class {
|
|
1949
|
+
credentials;
|
|
1950
|
+
sdk;
|
|
1951
|
+
signer;
|
|
1952
|
+
address;
|
|
1953
|
+
#storage;
|
|
1954
|
+
#onEvent;
|
|
1955
|
+
constructor(config) {
|
|
1956
|
+
const address = normalizeAddress(config.address, "address");
|
|
1957
|
+
this.credentials = config.credentials ?? new CredentialsManager({
|
|
1958
|
+
relayer: config.relayer,
|
|
1959
|
+
signer: config.signer,
|
|
1960
|
+
storage: config.storage,
|
|
1961
|
+
sessionStorage: config.sessionStorage,
|
|
1962
|
+
durationDays: config.durationDays ?? 1,
|
|
1963
|
+
onEvent: config.onEvent
|
|
1964
|
+
});
|
|
1965
|
+
this.sdk = config.relayer;
|
|
1966
|
+
this.signer = config.signer;
|
|
1967
|
+
this.address = address;
|
|
1968
|
+
this.#storage = config.storage;
|
|
1969
|
+
this.#onEvent = config.onEvent;
|
|
1970
|
+
}
|
|
1971
|
+
/** Access the storage backend (used by static batch methods). */
|
|
1972
|
+
get storage() {
|
|
1973
|
+
return this.#storage;
|
|
1974
|
+
}
|
|
1975
|
+
/** Emit a structured event (no-op when no listener is registered). */
|
|
1976
|
+
emit(partial) {
|
|
1977
|
+
this.#onEvent?.({ ...partial, tokenAddress: this.address, timestamp: Date.now() });
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Decrypt and return the plaintext balance for the given owner.
|
|
1981
|
+
* Generates FHE credentials automatically if they don't exist.
|
|
1982
|
+
*
|
|
1983
|
+
* @param owner - Optional balance owner address. Defaults to the connected signer.
|
|
1984
|
+
* @returns The decrypted plaintext balance as a bigint.
|
|
1985
|
+
* @throws {@link DecryptionFailedError} if FHE decryption fails.
|
|
1986
|
+
*
|
|
1987
|
+
* @example
|
|
1988
|
+
* ```ts
|
|
1989
|
+
* const balance = await token.balanceOf();
|
|
1990
|
+
* // or for another address:
|
|
1991
|
+
* const balance = await token.balanceOf("0xOwner");
|
|
1992
|
+
* ```
|
|
1993
|
+
*/
|
|
1994
|
+
async balanceOf(owner) {
|
|
1995
|
+
const ownerAddress = owner ? normalizeAddress(owner, "owner") : await this.signer.getAddress();
|
|
1996
|
+
const handle = await this.readConfidentialBalanceOf(ownerAddress);
|
|
1997
|
+
if (this.isZeroHandle(handle)) return BigInt(0);
|
|
1998
|
+
const cached = await loadCachedBalance({
|
|
1999
|
+
storage: this.#storage,
|
|
2000
|
+
tokenAddress: this.address,
|
|
2001
|
+
owner: ownerAddress,
|
|
2002
|
+
handle
|
|
2003
|
+
});
|
|
2004
|
+
if (cached !== null) return cached;
|
|
2005
|
+
const creds = await this.credentials.allow(this.address);
|
|
2006
|
+
const t0 = Date.now();
|
|
2007
|
+
try {
|
|
2008
|
+
this.emit({ type: ZamaSDKEvents.DecryptStart });
|
|
2009
|
+
const result = await this.sdk.userDecrypt({
|
|
2010
|
+
handles: [handle],
|
|
2011
|
+
contractAddress: this.address,
|
|
2012
|
+
signedContractAddresses: creds.contractAddresses,
|
|
2013
|
+
privateKey: creds.privateKey,
|
|
2014
|
+
publicKey: creds.publicKey,
|
|
2015
|
+
signature: creds.signature,
|
|
2016
|
+
signerAddress: ownerAddress,
|
|
2017
|
+
startTimestamp: creds.startTimestamp,
|
|
2018
|
+
durationDays: creds.durationDays
|
|
2019
|
+
});
|
|
2020
|
+
this.emit({ type: ZamaSDKEvents.DecryptEnd, durationMs: Date.now() - t0 });
|
|
2021
|
+
const value = result[handle] ?? BigInt(0);
|
|
2022
|
+
await saveCachedBalance({
|
|
2023
|
+
storage: this.#storage,
|
|
2024
|
+
tokenAddress: this.address,
|
|
2025
|
+
owner: ownerAddress,
|
|
2026
|
+
handle,
|
|
2027
|
+
value
|
|
2028
|
+
});
|
|
2029
|
+
return value;
|
|
2030
|
+
} catch (error) {
|
|
2031
|
+
this.emit({
|
|
2032
|
+
type: ZamaSDKEvents.DecryptError,
|
|
2033
|
+
error: toError(error),
|
|
2034
|
+
durationMs: Date.now() - t0
|
|
2035
|
+
});
|
|
2036
|
+
throw wrapDecryptError(error, "Failed to decrypt balance");
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
/**
|
|
2040
|
+
* Return the raw encrypted balance handle without decrypting.
|
|
2041
|
+
*
|
|
2042
|
+
* @param owner - Optional balance owner address. Defaults to the connected signer.
|
|
2043
|
+
* @returns The encrypted balance handle as a hex string.
|
|
2044
|
+
*
|
|
2045
|
+
* @example
|
|
2046
|
+
* ```ts
|
|
2047
|
+
* const handle = await token.confidentialBalanceOf();
|
|
2048
|
+
* ```
|
|
2049
|
+
*/
|
|
2050
|
+
async confidentialBalanceOf(owner) {
|
|
2051
|
+
const ownerAddress = owner ? normalizeAddress(owner, "owner") : await this.signer.getAddress();
|
|
2052
|
+
return this.readConfidentialBalanceOf(ownerAddress);
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* ERC-165 check for {@link ERC7984_INTERFACE_ID} support.
|
|
2056
|
+
*
|
|
2057
|
+
* @returns `true` if the contract implements the ERC-7984 confidential token interface.
|
|
2058
|
+
*
|
|
2059
|
+
* @example
|
|
2060
|
+
* ```ts
|
|
2061
|
+
* if (await token.isConfidential()) {
|
|
2062
|
+
* // Token supports encrypted operations
|
|
2063
|
+
* }
|
|
2064
|
+
* ```
|
|
2065
|
+
*/
|
|
2066
|
+
async isConfidential() {
|
|
2067
|
+
const result = await this.signer.readContract(
|
|
2068
|
+
supportsInterfaceContract(this.address, ERC7984_INTERFACE_ID)
|
|
2069
|
+
);
|
|
2070
|
+
return result === true;
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* ERC-165 check for {@link ERC7984_WRAPPER_INTERFACE_ID} support.
|
|
2074
|
+
*
|
|
2075
|
+
* @returns `true` if the contract implements the ERC-7984 wrapper interface.
|
|
2076
|
+
*
|
|
2077
|
+
* @example
|
|
2078
|
+
* ```ts
|
|
2079
|
+
* if (await token.isWrapper()) {
|
|
2080
|
+
* // Token is a confidential wrapper
|
|
2081
|
+
* }
|
|
2082
|
+
* ```
|
|
2083
|
+
*/
|
|
2084
|
+
async isWrapper() {
|
|
2085
|
+
const result = await this.signer.readContract(
|
|
2086
|
+
supportsInterfaceContract(this.address, ERC7984_WRAPPER_INTERFACE_ID)
|
|
2087
|
+
);
|
|
2088
|
+
return result === true;
|
|
2089
|
+
}
|
|
2090
|
+
/**
|
|
2091
|
+
* Decrypt multiple token balances in parallel.
|
|
2092
|
+
* When `handles` are provided, decrypts them directly (useful for two-phase
|
|
2093
|
+
* polling where handles are already known). When omitted, fetches handles
|
|
2094
|
+
* from the chain first.
|
|
2095
|
+
*
|
|
2096
|
+
* **Error handling:** If a per-token decryption fails and no `onError` callback
|
|
2097
|
+
* is provided, errors are collected and thrown as an aggregated
|
|
2098
|
+
* `DecryptionFailedError`. Pass `onError: () => 0n` for the old silent behavior.
|
|
2099
|
+
*
|
|
2100
|
+
* @param tokens - Array of ReadonlyToken instances to decrypt balances for.
|
|
2101
|
+
* @param options - Optional configuration for handles, owner, error handling, and concurrency.
|
|
2102
|
+
* @returns A Map from token address to decrypted balance.
|
|
2103
|
+
* @throws {@link DecryptionFailedError} if any decryption fails and no `onError` callback is provided.
|
|
2104
|
+
*
|
|
2105
|
+
* @example
|
|
2106
|
+
* ```ts
|
|
2107
|
+
* // Simple one-shot:
|
|
2108
|
+
* const balances = await ReadonlyToken.batchDecryptBalances(tokens);
|
|
2109
|
+
*
|
|
2110
|
+
* // With pre-fetched handles and error callback:
|
|
2111
|
+
* const handles = await Promise.all(tokens.map(t => t.confidentialBalanceOf()));
|
|
2112
|
+
* const balances = await ReadonlyToken.batchDecryptBalances(tokens, {
|
|
2113
|
+
* handles,
|
|
2114
|
+
* onError: (err, addr) => { console.error(addr, err); return 0n; },
|
|
2115
|
+
* });
|
|
2116
|
+
* ```
|
|
2117
|
+
*/
|
|
2118
|
+
static async batchDecryptBalances(tokens, options) {
|
|
2119
|
+
if (tokens.length === 0) return /* @__PURE__ */ new Map();
|
|
2120
|
+
const { handles, owner, onError, maxConcurrency } = options ?? {};
|
|
2121
|
+
const sdk = tokens[0].sdk;
|
|
2122
|
+
const signer = tokens[0].signer;
|
|
2123
|
+
const signerAddress = owner ?? await signer.getAddress();
|
|
2124
|
+
const resolvedHandles = handles ?? await Promise.all(tokens.map((t) => t.readConfidentialBalanceOf(signerAddress)));
|
|
2125
|
+
if (tokens.length !== resolvedHandles.length) {
|
|
2126
|
+
throw new DecryptionFailedError(
|
|
2127
|
+
`tokens.length (${tokens.length}) must equal handles.length (${resolvedHandles.length})`
|
|
2128
|
+
);
|
|
2129
|
+
}
|
|
2130
|
+
const allAddresses = tokens.map((t) => t.address);
|
|
2131
|
+
const creds = await tokens[0].credentials.allow(...allAddresses);
|
|
2132
|
+
const tokenStorage = tokens[0].storage;
|
|
2133
|
+
const results = /* @__PURE__ */ new Map();
|
|
2134
|
+
const errors = [];
|
|
2135
|
+
const decryptFns = [];
|
|
2136
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
2137
|
+
const token = tokens[i];
|
|
2138
|
+
const handle = resolvedHandles[i];
|
|
2139
|
+
if (token.isZeroHandle(handle)) {
|
|
2140
|
+
results.set(token.address, BigInt(0));
|
|
2141
|
+
continue;
|
|
2142
|
+
}
|
|
2143
|
+
const cached = await loadCachedBalance({
|
|
2144
|
+
storage: tokenStorage,
|
|
2145
|
+
tokenAddress: token.address,
|
|
2146
|
+
owner: signerAddress,
|
|
2147
|
+
handle
|
|
2148
|
+
});
|
|
2149
|
+
if (cached !== null) {
|
|
2150
|
+
results.set(token.address, cached);
|
|
2151
|
+
continue;
|
|
2152
|
+
}
|
|
2153
|
+
decryptFns.push(
|
|
2154
|
+
() => sdk.userDecrypt({
|
|
2155
|
+
handles: [handle],
|
|
2156
|
+
contractAddress: token.address,
|
|
2157
|
+
signedContractAddresses: creds.contractAddresses,
|
|
2158
|
+
privateKey: creds.privateKey,
|
|
2159
|
+
publicKey: creds.publicKey,
|
|
2160
|
+
signature: creds.signature,
|
|
2161
|
+
signerAddress,
|
|
2162
|
+
startTimestamp: creds.startTimestamp,
|
|
2163
|
+
durationDays: creds.durationDays
|
|
2164
|
+
}).then(async (result) => {
|
|
2165
|
+
const value = result[handle] ?? BigInt(0);
|
|
2166
|
+
results.set(token.address, value);
|
|
2167
|
+
await saveCachedBalance({
|
|
2168
|
+
storage: tokenStorage,
|
|
2169
|
+
tokenAddress: token.address,
|
|
2170
|
+
owner: signerAddress,
|
|
2171
|
+
handle,
|
|
2172
|
+
value
|
|
2173
|
+
});
|
|
2174
|
+
}).catch((error) => {
|
|
2175
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2176
|
+
if (onError) {
|
|
2177
|
+
results.set(token.address, onError(err, token.address));
|
|
2178
|
+
} else {
|
|
2179
|
+
errors.push({ address: token.address, error: err });
|
|
2180
|
+
}
|
|
2181
|
+
})
|
|
2182
|
+
);
|
|
2183
|
+
}
|
|
2184
|
+
await pLimit(decryptFns, maxConcurrency);
|
|
2185
|
+
if (errors.length > 0) {
|
|
2186
|
+
const message = errors.map((e) => `${e.address}: ${e.error.message}`).join("; ");
|
|
2187
|
+
throw new DecryptionFailedError(
|
|
2188
|
+
`Batch decryption failed for ${errors.length} token(s): ${message}`
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
return results;
|
|
2192
|
+
}
|
|
2193
|
+
/**
|
|
2194
|
+
* Look up the wrapper contract for this token via the deployment coordinator.
|
|
2195
|
+
* Returns `null` if no wrapper is deployed.
|
|
2196
|
+
*
|
|
2197
|
+
* @param coordinatorAddress - The deployment coordinator contract address.
|
|
2198
|
+
* @returns The wrapper address, or `null` if no wrapper exists.
|
|
2199
|
+
*
|
|
2200
|
+
* @example
|
|
2201
|
+
* ```ts
|
|
2202
|
+
* const wrapper = await token.discoverWrapper("0xCoordinator");
|
|
2203
|
+
* if (wrapper) {
|
|
2204
|
+
* const fullToken = sdk.createToken(token.address, wrapper);
|
|
2205
|
+
* }
|
|
2206
|
+
* ```
|
|
2207
|
+
*/
|
|
2208
|
+
async discoverWrapper(coordinatorAddress) {
|
|
2209
|
+
const coordinator = normalizeAddress(coordinatorAddress, "coordinatorAddress");
|
|
2210
|
+
const exists = await this.signer.readContract(
|
|
2211
|
+
wrapperExistsContract(coordinator, this.address)
|
|
2212
|
+
);
|
|
2213
|
+
if (!exists) return null;
|
|
2214
|
+
return this.signer.readContract(getWrapperContract(coordinator, this.address));
|
|
2215
|
+
}
|
|
2216
|
+
/**
|
|
2217
|
+
* Read the underlying ERC-20 address from this token's wrapper contract.
|
|
2218
|
+
*
|
|
2219
|
+
* @returns The underlying ERC-20 token address.
|
|
2220
|
+
*
|
|
2221
|
+
* @example
|
|
2222
|
+
* ```ts
|
|
2223
|
+
* const underlying = await token.underlyingToken();
|
|
2224
|
+
* ```
|
|
2225
|
+
*/
|
|
2226
|
+
async underlyingToken() {
|
|
2227
|
+
return this.signer.readContract(underlyingContract(this.address));
|
|
2228
|
+
}
|
|
2229
|
+
/**
|
|
2230
|
+
* Read the ERC-20 allowance of the underlying token for a given wrapper.
|
|
2231
|
+
*
|
|
2232
|
+
* @param wrapper - The wrapper contract address to check allowance for.
|
|
2233
|
+
* @param owner - Optional owner address. Defaults to the connected signer.
|
|
2234
|
+
* @returns The current allowance as a bigint.
|
|
2235
|
+
*
|
|
2236
|
+
* @example
|
|
2237
|
+
* ```ts
|
|
2238
|
+
* const allowance = await token.allowance("0xWrapper");
|
|
2239
|
+
* ```
|
|
2240
|
+
*/
|
|
2241
|
+
async allowance(wrapper, owner) {
|
|
2242
|
+
const normalizedWrapper = normalizeAddress(wrapper, "wrapper");
|
|
2243
|
+
const underlying = await this.signer.readContract(
|
|
2244
|
+
underlyingContract(normalizedWrapper)
|
|
2245
|
+
);
|
|
2246
|
+
const userAddress = owner ? normalizeAddress(owner, "owner") : await this.signer.getAddress();
|
|
2247
|
+
return this.signer.readContract(
|
|
2248
|
+
allowanceContract(underlying, userAddress, normalizedWrapper)
|
|
2249
|
+
);
|
|
2250
|
+
}
|
|
2251
|
+
/**
|
|
2252
|
+
* Read the token name from the contract.
|
|
2253
|
+
*
|
|
2254
|
+
* @returns The token name string.
|
|
2255
|
+
*
|
|
2256
|
+
* @example
|
|
2257
|
+
* ```ts
|
|
2258
|
+
* const name = await token.name(); // "Wrapped USDC"
|
|
2259
|
+
* ```
|
|
2260
|
+
*/
|
|
2261
|
+
async name() {
|
|
2262
|
+
return this.signer.readContract(nameContract(this.address));
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2265
|
+
* Read the token symbol from the contract.
|
|
2266
|
+
*
|
|
2267
|
+
* @returns The token symbol string.
|
|
2268
|
+
*
|
|
2269
|
+
* @example
|
|
2270
|
+
* ```ts
|
|
2271
|
+
* const symbol = await token.symbol(); // "cUSDC"
|
|
2272
|
+
* ```
|
|
2273
|
+
*/
|
|
2274
|
+
async symbol() {
|
|
2275
|
+
return this.signer.readContract(symbolContract(this.address));
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Read the token decimals from the contract.
|
|
2279
|
+
*
|
|
2280
|
+
* @returns The number of decimals.
|
|
2281
|
+
*
|
|
2282
|
+
* @example
|
|
2283
|
+
* ```ts
|
|
2284
|
+
* const decimals = await token.decimals(); // 6
|
|
2285
|
+
* ```
|
|
2286
|
+
*/
|
|
2287
|
+
async decimals() {
|
|
2288
|
+
return this.signer.readContract(decimalsContract(this.address));
|
|
2289
|
+
}
|
|
2290
|
+
/**
|
|
2291
|
+
* Ensure FHE decrypt credentials exist for this token.
|
|
2292
|
+
* Generates a keypair and requests an EIP-712 signature if needed.
|
|
2293
|
+
* Call this before any decrypt operation to avoid mid-flow wallet prompts.
|
|
2294
|
+
*
|
|
2295
|
+
* @returns Resolves when credentials are cached.
|
|
2296
|
+
*
|
|
2297
|
+
* @example
|
|
2298
|
+
* ```ts
|
|
2299
|
+
* await token.allow();
|
|
2300
|
+
* // Credentials are now cached — subsequent decrypts won't prompt
|
|
2301
|
+
* const balance = await token.balanceOf();
|
|
2302
|
+
* ```
|
|
2303
|
+
*/
|
|
2304
|
+
async allow() {
|
|
2305
|
+
await this.credentials.allow(this.address);
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Whether a session signature is currently cached for the connected wallet.
|
|
2309
|
+
* Use this to check if decrypt operations can proceed without a wallet prompt.
|
|
2310
|
+
*/
|
|
2311
|
+
async isAllowed() {
|
|
2312
|
+
return this.credentials.isAllowed();
|
|
2313
|
+
}
|
|
2314
|
+
/**
|
|
2315
|
+
* Revoke the session signature for the connected wallet.
|
|
2316
|
+
* Stored credentials remain intact, but the next decrypt operation
|
|
2317
|
+
* will require a fresh wallet signature.
|
|
2318
|
+
*/
|
|
2319
|
+
async revoke(...contractAddresses) {
|
|
2320
|
+
await this.credentials.revoke(...contractAddresses);
|
|
2321
|
+
}
|
|
2322
|
+
/**
|
|
2323
|
+
* Ensure FHE decrypt credentials exist for all given tokens in a single
|
|
2324
|
+
* wallet signature. Call this early (e.g. after loading the token list) so
|
|
2325
|
+
* that subsequent individual decrypt operations reuse cached credentials.
|
|
2326
|
+
*
|
|
2327
|
+
* @param tokens - Array of ReadonlyToken instances to allow.
|
|
2328
|
+
* @returns Resolves when all credentials are cached.
|
|
2329
|
+
*
|
|
2330
|
+
* @example
|
|
2331
|
+
* ```ts
|
|
2332
|
+
* const tokens = addresses.map(a => sdk.createReadonlyToken(a));
|
|
2333
|
+
* await ReadonlyToken.allow(...tokens);
|
|
2334
|
+
* // All tokens now share the same credentials
|
|
2335
|
+
* ```
|
|
2336
|
+
*/
|
|
2337
|
+
static async allow(...tokens) {
|
|
2338
|
+
if (tokens.length === 0) return;
|
|
2339
|
+
const allAddresses = tokens.map((t) => t.address);
|
|
2340
|
+
await tokens[0].credentials.allow(...allAddresses);
|
|
2341
|
+
}
|
|
2342
|
+
async readConfidentialBalanceOf(owner) {
|
|
2343
|
+
const result = await this.signer.readContract(
|
|
2344
|
+
confidentialBalanceOfContract(this.address, owner)
|
|
2345
|
+
);
|
|
2346
|
+
return this.normalizeHandle(result);
|
|
2347
|
+
}
|
|
2348
|
+
normalizeHandle(value) {
|
|
2349
|
+
if (typeof value === "string" && value.startsWith("0x")) {
|
|
2350
|
+
return value;
|
|
2351
|
+
}
|
|
2352
|
+
if (typeof value === "bigint") {
|
|
2353
|
+
return `0x${value.toString(16).padStart(64, "0")}`;
|
|
2354
|
+
}
|
|
2355
|
+
return ZERO_HANDLE;
|
|
2356
|
+
}
|
|
2357
|
+
isZeroHandle(handle) {
|
|
2358
|
+
return handle === ZERO_HANDLE || handle === "0x";
|
|
2359
|
+
}
|
|
2360
|
+
/**
|
|
2361
|
+
* Decrypt a single encrypted handle into a plaintext bigint.
|
|
2362
|
+
* Returns `0n` for zero handles without calling the relayer.
|
|
2363
|
+
*
|
|
2364
|
+
* @param handle - The encrypted balance handle to decrypt.
|
|
2365
|
+
* @param owner - Optional owner address for the decrypt request.
|
|
2366
|
+
* @returns The decrypted plaintext value as a bigint.
|
|
2367
|
+
* @throws {@link DecryptionFailedError} if FHE decryption fails.
|
|
2368
|
+
*
|
|
2369
|
+
* @example
|
|
2370
|
+
* ```ts
|
|
2371
|
+
* const handle = await token.confidentialBalanceOf();
|
|
2372
|
+
* const value = await token.decryptBalance(handle);
|
|
2373
|
+
* ```
|
|
2374
|
+
*/
|
|
2375
|
+
async decryptBalance(handle, owner) {
|
|
2376
|
+
if (this.isZeroHandle(handle)) return BigInt(0);
|
|
2377
|
+
const signerAddress = owner ?? await this.signer.getAddress();
|
|
2378
|
+
const cached = await loadCachedBalance({
|
|
2379
|
+
storage: this.#storage,
|
|
2380
|
+
tokenAddress: this.address,
|
|
2381
|
+
owner: signerAddress,
|
|
2382
|
+
handle
|
|
2383
|
+
});
|
|
2384
|
+
if (cached !== null) return cached;
|
|
2385
|
+
const creds = await this.credentials.allow(this.address);
|
|
2386
|
+
const t0 = Date.now();
|
|
2387
|
+
try {
|
|
2388
|
+
this.emit({ type: ZamaSDKEvents.DecryptStart });
|
|
2389
|
+
const result = await this.sdk.userDecrypt({
|
|
2390
|
+
handles: [handle],
|
|
2391
|
+
contractAddress: this.address,
|
|
2392
|
+
signedContractAddresses: creds.contractAddresses,
|
|
2393
|
+
privateKey: creds.privateKey,
|
|
2394
|
+
publicKey: creds.publicKey,
|
|
2395
|
+
signature: creds.signature,
|
|
2396
|
+
signerAddress,
|
|
2397
|
+
startTimestamp: creds.startTimestamp,
|
|
2398
|
+
durationDays: creds.durationDays
|
|
2399
|
+
});
|
|
2400
|
+
this.emit({ type: ZamaSDKEvents.DecryptEnd, durationMs: Date.now() - t0 });
|
|
2401
|
+
const value = result[handle] ?? BigInt(0);
|
|
2402
|
+
await saveCachedBalance({
|
|
2403
|
+
storage: this.#storage,
|
|
2404
|
+
tokenAddress: this.address,
|
|
2405
|
+
owner: signerAddress,
|
|
2406
|
+
handle,
|
|
2407
|
+
value
|
|
2408
|
+
});
|
|
2409
|
+
return value;
|
|
2410
|
+
} catch (error) {
|
|
2411
|
+
this.emit({
|
|
2412
|
+
type: ZamaSDKEvents.DecryptError,
|
|
2413
|
+
error: toError(error),
|
|
2414
|
+
durationMs: Date.now() - t0
|
|
2415
|
+
});
|
|
2416
|
+
throw wrapDecryptError(error, "Failed to decrypt balance");
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* Batch-decrypt arbitrary encrypted handles in a single relayer call.
|
|
2421
|
+
* Zero handles are returned as 0n without hitting the relayer.
|
|
2422
|
+
*
|
|
2423
|
+
* @param handles - Array of encrypted handles to decrypt.
|
|
2424
|
+
* @param owner - Optional owner address for the decrypt request.
|
|
2425
|
+
* @returns A Map from handle to decrypted bigint value.
|
|
2426
|
+
* @throws {@link DecryptionFailedError} if FHE decryption fails.
|
|
2427
|
+
*/
|
|
2428
|
+
async decryptHandles(handles, owner) {
|
|
2429
|
+
const results = /* @__PURE__ */ new Map();
|
|
2430
|
+
const nonZeroHandles = [];
|
|
2431
|
+
for (const handle of handles) {
|
|
2432
|
+
if (this.isZeroHandle(handle)) {
|
|
2433
|
+
results.set(handle, BigInt(0));
|
|
2434
|
+
} else {
|
|
2435
|
+
nonZeroHandles.push(handle);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
if (nonZeroHandles.length === 0) return results;
|
|
2439
|
+
const creds = await this.credentials.allow(this.address);
|
|
2440
|
+
const t0 = Date.now();
|
|
2441
|
+
try {
|
|
2442
|
+
this.emit({ type: ZamaSDKEvents.DecryptStart });
|
|
2443
|
+
const decrypted = await this.sdk.userDecrypt({
|
|
2444
|
+
handles: nonZeroHandles,
|
|
2445
|
+
contractAddress: this.address,
|
|
2446
|
+
signedContractAddresses: creds.contractAddresses,
|
|
2447
|
+
privateKey: creds.privateKey,
|
|
2448
|
+
publicKey: creds.publicKey,
|
|
2449
|
+
signature: creds.signature,
|
|
2450
|
+
signerAddress: owner ?? await this.signer.getAddress(),
|
|
2451
|
+
startTimestamp: creds.startTimestamp,
|
|
2452
|
+
durationDays: creds.durationDays
|
|
2453
|
+
});
|
|
2454
|
+
this.emit({ type: ZamaSDKEvents.DecryptEnd, durationMs: Date.now() - t0 });
|
|
2455
|
+
for (const handle of nonZeroHandles) {
|
|
2456
|
+
results.set(handle, decrypted[handle] ?? BigInt(0));
|
|
2457
|
+
}
|
|
2458
|
+
} catch (error) {
|
|
2459
|
+
this.emit({
|
|
2460
|
+
type: ZamaSDKEvents.DecryptError,
|
|
2461
|
+
error: toError(error),
|
|
2462
|
+
durationMs: Date.now() - t0
|
|
2463
|
+
});
|
|
2464
|
+
throw wrapDecryptError(error, "Failed to decrypt handles");
|
|
2465
|
+
}
|
|
2466
|
+
return results;
|
|
2467
|
+
}
|
|
2468
|
+
};
|
|
2469
|
+
function toError(error) {
|
|
2470
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
2471
|
+
}
|
|
2472
|
+
function wrapDecryptError(error, fallbackMessage) {
|
|
2473
|
+
if (error instanceof NoCiphertextError || error instanceof RelayerRequestFailedError) {
|
|
2474
|
+
return error;
|
|
2475
|
+
}
|
|
2476
|
+
const statusCode = error != null && typeof error === "object" && "statusCode" in error && typeof error.statusCode === "number" ? error.statusCode : void 0;
|
|
2477
|
+
if (statusCode === 400) {
|
|
2478
|
+
return new NoCiphertextError(
|
|
2479
|
+
error instanceof Error ? error.message : "No ciphertext for this account",
|
|
2480
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
2481
|
+
);
|
|
2482
|
+
}
|
|
2483
|
+
if (statusCode !== void 0) {
|
|
2484
|
+
return new RelayerRequestFailedError(
|
|
2485
|
+
error instanceof Error ? error.message : fallbackMessage,
|
|
2486
|
+
statusCode,
|
|
2487
|
+
{ cause: error instanceof Error ? error : void 0 }
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
return new DecryptionFailedError(fallbackMessage, {
|
|
2491
|
+
cause: error instanceof Error ? error : void 0
|
|
2492
|
+
});
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
// src/token/token.ts
|
|
2496
|
+
function toError2(error) {
|
|
2497
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
2498
|
+
}
|
|
2499
|
+
var Token = class _Token extends ReadonlyToken {
|
|
2500
|
+
static ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
2501
|
+
wrapper;
|
|
2502
|
+
#underlying;
|
|
2503
|
+
#underlyingPromise = null;
|
|
2504
|
+
constructor(config) {
|
|
2505
|
+
super(config);
|
|
2506
|
+
this.wrapper = config.wrapper ? normalizeAddress(config.wrapper, "wrapper") : this.address;
|
|
2507
|
+
}
|
|
2508
|
+
async #getUnderlying() {
|
|
2509
|
+
if (this.#underlying !== void 0) return this.#underlying;
|
|
2510
|
+
if (!this.#underlyingPromise) {
|
|
2511
|
+
this.#underlyingPromise = this.signer.readContract(underlyingContract(this.wrapper)).then((v) => {
|
|
2512
|
+
this.#underlying = v;
|
|
2513
|
+
this.#underlyingPromise = null;
|
|
2514
|
+
return v;
|
|
2515
|
+
}).catch((error) => {
|
|
2516
|
+
this.#underlyingPromise = null;
|
|
2517
|
+
throw error;
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
return this.#underlyingPromise;
|
|
2521
|
+
}
|
|
2522
|
+
// WRITE OPERATIONS
|
|
2523
|
+
/**
|
|
2524
|
+
* Confidential transfer. Encrypts the amount via FHE, then calls the contract.
|
|
2525
|
+
* Returns the transaction hash.
|
|
2526
|
+
*
|
|
2527
|
+
* @param to - Recipient address.
|
|
2528
|
+
* @param amount - Plaintext amount to transfer (encrypted automatically via FHE).
|
|
2529
|
+
* @returns The transaction hash and mined receipt.
|
|
2530
|
+
* @throws {@link EncryptionFailedError} if FHE encryption fails.
|
|
2531
|
+
* @throws {@link TransactionRevertedError} if the on-chain transfer reverts.
|
|
2532
|
+
*
|
|
2533
|
+
* @example
|
|
2534
|
+
* ```ts
|
|
2535
|
+
* const txHash = await token.confidentialTransfer("0xRecipient", 1000n);
|
|
2536
|
+
* ```
|
|
2537
|
+
*/
|
|
2538
|
+
async confidentialTransfer(to, amount, callbacks) {
|
|
2539
|
+
const normalizedTo = normalizeAddress(to, "to");
|
|
2540
|
+
let handles;
|
|
2541
|
+
let inputProof;
|
|
2542
|
+
const t0 = Date.now();
|
|
2543
|
+
try {
|
|
2544
|
+
this.emit({ type: ZamaSDKEvents.EncryptStart });
|
|
2545
|
+
({ handles, inputProof } = await this.sdk.encrypt({
|
|
2546
|
+
values: [{ value: amount, type: "euint64" }],
|
|
2547
|
+
contractAddress: this.address,
|
|
2548
|
+
userAddress: await this.signer.getAddress()
|
|
2549
|
+
}));
|
|
2550
|
+
this.emit({ type: ZamaSDKEvents.EncryptEnd, durationMs: Date.now() - t0 });
|
|
2551
|
+
safeCallback(() => callbacks?.onEncryptComplete?.());
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
this.emit({
|
|
2554
|
+
type: ZamaSDKEvents.EncryptError,
|
|
2555
|
+
error: toError2(error),
|
|
2556
|
+
durationMs: Date.now() - t0
|
|
2557
|
+
});
|
|
2558
|
+
if (error instanceof ZamaError) throw error;
|
|
2559
|
+
throw new EncryptionFailedError("Failed to encrypt transfer amount", {
|
|
2560
|
+
cause: error instanceof Error ? error : void 0
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
if (handles.length === 0) {
|
|
2564
|
+
throw new EncryptionFailedError("Encryption returned no handles");
|
|
2565
|
+
}
|
|
2566
|
+
try {
|
|
2567
|
+
const txHash = await this.signer.writeContract(
|
|
2568
|
+
confidentialTransferContract(this.address, normalizedTo, handles[0], inputProof)
|
|
2569
|
+
);
|
|
2570
|
+
this.emit({ type: ZamaSDKEvents.TransferSubmitted, txHash });
|
|
2571
|
+
safeCallback(() => callbacks?.onTransferSubmitted?.(txHash));
|
|
2572
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
2573
|
+
return { txHash, receipt };
|
|
2574
|
+
} catch (error) {
|
|
2575
|
+
this.emit({
|
|
2576
|
+
type: ZamaSDKEvents.TransactionError,
|
|
2577
|
+
operation: "transfer",
|
|
2578
|
+
error: toError2(error)
|
|
2579
|
+
});
|
|
2580
|
+
if (error instanceof ZamaError) throw error;
|
|
2581
|
+
throw new TransactionRevertedError("Transfer transaction failed", {
|
|
2582
|
+
cause: error instanceof Error ? error : void 0
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
/**
|
|
2587
|
+
* Operator encrypted transfer on behalf of another address.
|
|
2588
|
+
* The caller must be an approved operator for `from`.
|
|
2589
|
+
*
|
|
2590
|
+
* @param from - The address to transfer from (caller must be an approved operator).
|
|
2591
|
+
* @param to - Recipient address.
|
|
2592
|
+
* @param amount - Plaintext amount to transfer (encrypted automatically via FHE).
|
|
2593
|
+
* @returns The transaction hash and mined receipt.
|
|
2594
|
+
* @throws {@link EncryptionFailedError} if FHE encryption fails.
|
|
2595
|
+
* @throws {@link TransactionRevertedError} if the on-chain transfer reverts.
|
|
2596
|
+
*
|
|
2597
|
+
* @example
|
|
2598
|
+
* ```ts
|
|
2599
|
+
* const txHash = await token.confidentialTransferFrom("0xFrom", "0xTo", 500n);
|
|
2600
|
+
* ```
|
|
2601
|
+
*/
|
|
2602
|
+
async confidentialTransferFrom(from, to, amount, callbacks) {
|
|
2603
|
+
const normalizedFrom = normalizeAddress(from, "from");
|
|
2604
|
+
const normalizedTo = normalizeAddress(to, "to");
|
|
2605
|
+
let handles;
|
|
2606
|
+
let inputProof;
|
|
2607
|
+
const t0 = Date.now();
|
|
2608
|
+
try {
|
|
2609
|
+
this.emit({ type: ZamaSDKEvents.EncryptStart });
|
|
2610
|
+
({ handles, inputProof } = await this.sdk.encrypt({
|
|
2611
|
+
values: [{ value: amount, type: "euint64" }],
|
|
2612
|
+
contractAddress: this.address,
|
|
2613
|
+
userAddress: normalizedFrom
|
|
2614
|
+
}));
|
|
2615
|
+
this.emit({ type: ZamaSDKEvents.EncryptEnd, durationMs: Date.now() - t0 });
|
|
2616
|
+
safeCallback(() => callbacks?.onEncryptComplete?.());
|
|
2617
|
+
} catch (error) {
|
|
2618
|
+
this.emit({
|
|
2619
|
+
type: ZamaSDKEvents.EncryptError,
|
|
2620
|
+
error: toError2(error),
|
|
2621
|
+
durationMs: Date.now() - t0
|
|
2622
|
+
});
|
|
2623
|
+
if (error instanceof ZamaError) throw error;
|
|
2624
|
+
throw new EncryptionFailedError("Failed to encrypt transferFrom amount", {
|
|
2625
|
+
cause: error instanceof Error ? error : void 0
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
if (handles.length === 0) {
|
|
2629
|
+
throw new EncryptionFailedError("Encryption returned no handles");
|
|
2630
|
+
}
|
|
2631
|
+
try {
|
|
2632
|
+
const txHash = await this.signer.writeContract(
|
|
2633
|
+
confidentialTransferFromContract(
|
|
2634
|
+
this.address,
|
|
2635
|
+
normalizedFrom,
|
|
2636
|
+
normalizedTo,
|
|
2637
|
+
handles[0],
|
|
2638
|
+
inputProof
|
|
2639
|
+
)
|
|
2640
|
+
);
|
|
2641
|
+
this.emit({ type: ZamaSDKEvents.TransferFromSubmitted, txHash });
|
|
2642
|
+
safeCallback(() => callbacks?.onTransferSubmitted?.(txHash));
|
|
2643
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
2644
|
+
return { txHash, receipt };
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
this.emit({
|
|
2647
|
+
type: ZamaSDKEvents.TransactionError,
|
|
2648
|
+
operation: "transferFrom",
|
|
2649
|
+
error: toError2(error)
|
|
2650
|
+
});
|
|
2651
|
+
if (error instanceof ZamaError) throw error;
|
|
2652
|
+
throw new TransactionRevertedError("TransferFrom transaction failed", {
|
|
2653
|
+
cause: error instanceof Error ? error : void 0
|
|
2654
|
+
});
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
/**
|
|
2658
|
+
* Set operator approval for the confidential token.
|
|
2659
|
+
* Defaults to 1 hour from now if `until` is not specified.
|
|
2660
|
+
*
|
|
2661
|
+
* @param spender - The address to approve as an operator.
|
|
2662
|
+
* @param until - Optional Unix timestamp for approval expiry. Defaults to now + 1 hour.
|
|
2663
|
+
* @returns The transaction hash and mined receipt.
|
|
2664
|
+
* @throws {@link ApprovalFailedError} if the approval transaction fails.
|
|
2665
|
+
*
|
|
2666
|
+
* @example
|
|
2667
|
+
* ```ts
|
|
2668
|
+
* const txHash = await token.approve("0xSpender");
|
|
2669
|
+
* ```
|
|
2670
|
+
*/
|
|
2671
|
+
async approve(spender, until) {
|
|
2672
|
+
const normalizedSpender = normalizeAddress(spender, "spender");
|
|
2673
|
+
try {
|
|
2674
|
+
const txHash = await this.signer.writeContract(
|
|
2675
|
+
setOperatorContract(this.address, normalizedSpender, until)
|
|
2676
|
+
);
|
|
2677
|
+
this.emit({ type: ZamaSDKEvents.ApproveSubmitted, txHash });
|
|
2678
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
2679
|
+
return { txHash, receipt };
|
|
2680
|
+
} catch (error) {
|
|
2681
|
+
this.emit({
|
|
2682
|
+
type: ZamaSDKEvents.TransactionError,
|
|
2683
|
+
operation: "approve",
|
|
2684
|
+
error: toError2(error)
|
|
2685
|
+
});
|
|
2686
|
+
if (error instanceof ZamaError) throw error;
|
|
2687
|
+
throw new ApprovalFailedError("Operator approval failed", {
|
|
2688
|
+
cause: error instanceof Error ? error : void 0
|
|
2689
|
+
});
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
/**
|
|
2693
|
+
* Check if a spender is an approved operator for a given holder.
|
|
2694
|
+
*
|
|
2695
|
+
* @param spender - The address to check operator approval for.
|
|
2696
|
+
* @param holder - The token holder address. Defaults to the connected wallet.
|
|
2697
|
+
* @returns `true` if the spender is an approved operator for the holder.
|
|
2698
|
+
*
|
|
2699
|
+
* @example
|
|
2700
|
+
* ```ts
|
|
2701
|
+
* if (await token.isApproved("0xSpender")) {
|
|
2702
|
+
* // spender can call transferFrom on behalf of connected wallet
|
|
2703
|
+
* }
|
|
2704
|
+
* // or check for a specific holder:
|
|
2705
|
+
* if (await token.isApproved("0xSpender", "0xHolder")) { ... }
|
|
2706
|
+
* ```
|
|
2707
|
+
*/
|
|
2708
|
+
async isApproved(spender, holder) {
|
|
2709
|
+
const normalizedSpender = normalizeAddress(spender, "spender");
|
|
2710
|
+
const resolvedHolder = holder ? normalizeAddress(holder, "holder") : await this.signer.getAddress();
|
|
2711
|
+
return this.signer.readContract(
|
|
2712
|
+
isOperatorContract(this.address, resolvedHolder, normalizedSpender)
|
|
2713
|
+
);
|
|
2714
|
+
}
|
|
2715
|
+
/**
|
|
2716
|
+
* Shield public ERC-20 tokens into confidential tokens.
|
|
2717
|
+
* Handles ERC-20 approval automatically based on `approvalStrategy`
|
|
2718
|
+
* (`"exact"` by default, `"max"` for unlimited approval, `"skip"` to opt out).
|
|
2719
|
+
*
|
|
2720
|
+
* @param amount - The plaintext amount to shield.
|
|
2721
|
+
* @param options - Optional configuration: `approvalStrategy` (`"exact"` | `"max"` | `"skip"`, default `"exact"`), `fees` (extra ETH for native wrappers).
|
|
2722
|
+
* @returns The transaction hash and mined receipt.
|
|
2723
|
+
* @throws {@link ApprovalFailedError} if the ERC-20 approval step fails.
|
|
2724
|
+
* @throws {@link TransactionRevertedError} if the shield transaction reverts.
|
|
2725
|
+
*
|
|
2726
|
+
* @example
|
|
2727
|
+
* ```ts
|
|
2728
|
+
* const txHash = await token.shield(1000n);
|
|
2729
|
+
* // or with exact approval:
|
|
2730
|
+
* const txHash = await token.shield(1000n, { approvalStrategy: "exact" });
|
|
2731
|
+
* ```
|
|
2732
|
+
*/
|
|
2733
|
+
async shield(amount, options) {
|
|
2734
|
+
const underlying = await this.#getUnderlying();
|
|
2735
|
+
if (underlying === _Token.ZERO_ADDRESS) {
|
|
2736
|
+
return this.shieldETH(amount, amount + (options?.fees ?? 0n));
|
|
2737
|
+
}
|
|
2738
|
+
const strategy = options?.approvalStrategy ?? "exact";
|
|
2739
|
+
if (strategy !== "skip") {
|
|
2740
|
+
await this.#ensureAllowance(amount, strategy === "max", options?.callbacks);
|
|
2741
|
+
}
|
|
2742
|
+
try {
|
|
2743
|
+
const recipient = options?.to ? normalizeAddress(options.to, "to") : await this.signer.getAddress();
|
|
2744
|
+
const txHash = await this.signer.writeContract(wrapContract(this.wrapper, recipient, amount));
|
|
2745
|
+
this.emit({ type: ZamaSDKEvents.ShieldSubmitted, txHash });
|
|
2746
|
+
safeCallback(() => options?.callbacks?.onShieldSubmitted?.(txHash));
|
|
2747
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
2748
|
+
return { txHash, receipt };
|
|
2749
|
+
} catch (error) {
|
|
2750
|
+
this.emit({
|
|
2751
|
+
type: ZamaSDKEvents.TransactionError,
|
|
2752
|
+
operation: "shield",
|
|
2753
|
+
error: toError2(error)
|
|
2754
|
+
});
|
|
2755
|
+
if (error instanceof ZamaError) throw error;
|
|
2756
|
+
throw new TransactionRevertedError("Shield transaction failed", {
|
|
2757
|
+
cause: error instanceof Error ? error : void 0
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
/**
|
|
2762
|
+
* Shield native ETH into confidential tokens. `value` defaults to `amount`.
|
|
2763
|
+
*
|
|
2764
|
+
* @param amount - The amount of ETH to shield (in wei).
|
|
2765
|
+
* @param value - Optional ETH value to send. Defaults to `amount`.
|
|
2766
|
+
* @returns The transaction hash and mined receipt.
|
|
2767
|
+
* @throws {@link TransactionRevertedError} if the shield transaction reverts.
|
|
2768
|
+
*
|
|
2769
|
+
* @example
|
|
2770
|
+
* ```ts
|
|
2771
|
+
* const txHash = await token.shieldETH(1000000000000000000n); // 1 ETH
|
|
2772
|
+
* ```
|
|
2773
|
+
*/
|
|
2774
|
+
async shieldETH(amount, value) {
|
|
2775
|
+
try {
|
|
2776
|
+
const userAddress = await this.signer.getAddress();
|
|
2777
|
+
const txHash = await this.signer.writeContract(
|
|
2778
|
+
wrapETHContract(this.wrapper, userAddress, amount, value ?? amount)
|
|
2779
|
+
);
|
|
2780
|
+
this.emit({ type: ZamaSDKEvents.ShieldSubmitted, txHash });
|
|
2781
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
2782
|
+
return { txHash, receipt };
|
|
2783
|
+
} catch (error) {
|
|
2784
|
+
this.emit({
|
|
2785
|
+
type: ZamaSDKEvents.TransactionError,
|
|
2786
|
+
operation: "shieldETH",
|
|
2787
|
+
error: toError2(error)
|
|
2788
|
+
});
|
|
2789
|
+
if (error instanceof ZamaError) throw error;
|
|
2790
|
+
throw new TransactionRevertedError("Shield ETH transaction failed", {
|
|
2791
|
+
cause: error instanceof Error ? error : void 0
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
/**
|
|
2796
|
+
* Request an unwrap for a specific amount. Encrypts the amount first.
|
|
2797
|
+
* Call {@link finalizeUnwrap} after the request is processed on-chain.
|
|
2798
|
+
*
|
|
2799
|
+
* @param amount - The plaintext amount to unwrap (encrypted automatically).
|
|
2800
|
+
* @returns The transaction hash and mined receipt.
|
|
2801
|
+
* @throws {@link EncryptionFailedError} if FHE encryption fails.
|
|
2802
|
+
* @throws {@link TransactionRevertedError} if the unwrap transaction reverts.
|
|
2803
|
+
*
|
|
2804
|
+
* @example
|
|
2805
|
+
* ```ts
|
|
2806
|
+
* const txHash = await token.unwrap(500n);
|
|
2807
|
+
* ```
|
|
2808
|
+
*/
|
|
2809
|
+
async unwrap(amount) {
|
|
2810
|
+
const userAddress = await this.signer.getAddress();
|
|
2811
|
+
let handles;
|
|
2812
|
+
let inputProof;
|
|
2813
|
+
const t0 = Date.now();
|
|
2814
|
+
try {
|
|
2815
|
+
this.emit({ type: ZamaSDKEvents.EncryptStart });
|
|
2816
|
+
({ handles, inputProof } = await this.sdk.encrypt({
|
|
2817
|
+
values: [{ value: amount, type: "euint64" }],
|
|
2818
|
+
contractAddress: this.wrapper,
|
|
2819
|
+
userAddress
|
|
2820
|
+
}));
|
|
2821
|
+
this.emit({ type: ZamaSDKEvents.EncryptEnd, durationMs: Date.now() - t0 });
|
|
2822
|
+
} catch (error) {
|
|
2823
|
+
this.emit({
|
|
2824
|
+
type: ZamaSDKEvents.EncryptError,
|
|
2825
|
+
error: toError2(error),
|
|
2826
|
+
durationMs: Date.now() - t0
|
|
2827
|
+
});
|
|
2828
|
+
if (error instanceof ZamaError) throw error;
|
|
2829
|
+
throw new EncryptionFailedError("Failed to encrypt unshield amount", {
|
|
2830
|
+
cause: error instanceof Error ? error : void 0
|
|
2831
|
+
});
|
|
2832
|
+
}
|
|
2833
|
+
if (handles.length === 0) {
|
|
2834
|
+
throw new EncryptionFailedError("Encryption returned no handles");
|
|
2835
|
+
}
|
|
2836
|
+
try {
|
|
2837
|
+
const txHash = await this.signer.writeContract(
|
|
2838
|
+
unwrapContract(this.address, userAddress, userAddress, handles[0], inputProof)
|
|
2839
|
+
);
|
|
2840
|
+
this.emit({ type: ZamaSDKEvents.UnwrapSubmitted, txHash });
|
|
2841
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
2842
|
+
return { txHash, receipt };
|
|
2843
|
+
} catch (error) {
|
|
2844
|
+
this.emit({
|
|
2845
|
+
type: ZamaSDKEvents.TransactionError,
|
|
2846
|
+
operation: "unwrap",
|
|
2847
|
+
error: toError2(error)
|
|
2848
|
+
});
|
|
2849
|
+
if (error instanceof ZamaError) throw error;
|
|
2850
|
+
throw new TransactionRevertedError("Unshield transaction failed", {
|
|
2851
|
+
cause: error instanceof Error ? error : void 0
|
|
2852
|
+
});
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
/**
|
|
2856
|
+
* Request an unwrap for the entire confidential balance.
|
|
2857
|
+
* Uses the on-chain balance handle directly (no encryption needed).
|
|
2858
|
+
* Throws if the balance is zero.
|
|
2859
|
+
*
|
|
2860
|
+
* @returns The transaction hash and mined receipt.
|
|
2861
|
+
* @throws {@link DecryptionFailedError} if the balance is zero.
|
|
2862
|
+
* @throws {@link TransactionRevertedError} if the unwrap transaction reverts.
|
|
2863
|
+
*
|
|
2864
|
+
* @example
|
|
2865
|
+
* ```ts
|
|
2866
|
+
* const txHash = await token.unwrapAll();
|
|
2867
|
+
* ```
|
|
2868
|
+
*/
|
|
2869
|
+
async unwrapAll() {
|
|
2870
|
+
const userAddress = await this.signer.getAddress();
|
|
2871
|
+
const handle = await this.readConfidentialBalanceOf(userAddress);
|
|
2872
|
+
if (this.isZeroHandle(handle)) {
|
|
2873
|
+
throw new DecryptionFailedError("Cannot unshield: balance is zero");
|
|
2874
|
+
}
|
|
2875
|
+
try {
|
|
2876
|
+
const txHash = await this.signer.writeContract(
|
|
2877
|
+
unwrapFromBalanceContract(this.address, userAddress, userAddress, handle)
|
|
2878
|
+
);
|
|
2879
|
+
this.emit({ type: ZamaSDKEvents.UnwrapSubmitted, txHash });
|
|
2880
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
2881
|
+
return { txHash, receipt };
|
|
2882
|
+
} catch (error) {
|
|
2883
|
+
this.emit({
|
|
2884
|
+
type: ZamaSDKEvents.TransactionError,
|
|
2885
|
+
operation: "unwrap",
|
|
2886
|
+
error: toError2(error)
|
|
2887
|
+
});
|
|
2888
|
+
if (error instanceof ZamaError) throw error;
|
|
2889
|
+
throw new TransactionRevertedError("Unshield-all transaction failed", {
|
|
2890
|
+
cause: error instanceof Error ? error : void 0
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* Unshield a specific amount and finalize in one call.
|
|
2896
|
+
* Orchestrates: unshield → wait for receipt → parse event → finalize.
|
|
2897
|
+
*
|
|
2898
|
+
* @param amount - The plaintext amount to unshield.
|
|
2899
|
+
* @param callbacks - Optional progress callbacks for each phase.
|
|
2900
|
+
* @returns The finalize transaction hash and mined receipt.
|
|
2901
|
+
* @throws {@link EncryptionFailedError} if FHE encryption fails.
|
|
2902
|
+
* @throws {@link TransactionRevertedError} if any transaction in the flow reverts.
|
|
2903
|
+
*
|
|
2904
|
+
* @example
|
|
2905
|
+
* ```ts
|
|
2906
|
+
* const txHash = await token.unshield(500n);
|
|
2907
|
+
* ```
|
|
2908
|
+
*/
|
|
2909
|
+
async unshield(amount, callbacks) {
|
|
2910
|
+
const operationId = crypto.randomUUID();
|
|
2911
|
+
const unwrapResult = await this.unwrap(amount);
|
|
2912
|
+
safeCallback(() => callbacks?.onUnwrapSubmitted?.(unwrapResult.txHash));
|
|
2913
|
+
return this.#waitAndFinalizeUnshield(unwrapResult.txHash, callbacks, operationId);
|
|
2914
|
+
}
|
|
2915
|
+
/**
|
|
2916
|
+
* Unshield the entire balance and finalize in one call.
|
|
2917
|
+
* Orchestrates: unshieldAll → wait for receipt → parse event → finalize.
|
|
2918
|
+
*
|
|
2919
|
+
* @param callbacks - Optional progress callbacks for each phase.
|
|
2920
|
+
* @returns The finalize transaction hash and mined receipt.
|
|
2921
|
+
* @throws {@link DecryptionFailedError} if the balance is zero.
|
|
2922
|
+
* @throws {@link TransactionRevertedError} if any transaction in the flow reverts.
|
|
2923
|
+
*
|
|
2924
|
+
* @example
|
|
2925
|
+
* ```ts
|
|
2926
|
+
* const txHash = await token.unshieldAll();
|
|
2927
|
+
* ```
|
|
2928
|
+
*/
|
|
2929
|
+
async unshieldAll(callbacks) {
|
|
2930
|
+
const operationId = crypto.randomUUID();
|
|
2931
|
+
const unwrapResult = await this.unwrapAll();
|
|
2932
|
+
safeCallback(() => callbacks?.onUnwrapSubmitted?.(unwrapResult.txHash));
|
|
2933
|
+
return this.#waitAndFinalizeUnshield(unwrapResult.txHash, callbacks, operationId);
|
|
2934
|
+
}
|
|
2935
|
+
/**
|
|
2936
|
+
* Resume an in-progress unshield from an existing unwrap tx hash.
|
|
2937
|
+
* Useful when the user already submitted the unwrap but the finalize step
|
|
2938
|
+
* was interrupted (e.g. page reload, network error).
|
|
2939
|
+
*
|
|
2940
|
+
* @param unwrapTxHash - The transaction hash of the previously submitted unwrap.
|
|
2941
|
+
* @param callbacks - Optional progress callbacks.
|
|
2942
|
+
* @returns The finalize transaction hash and mined receipt.
|
|
2943
|
+
* @throws {@link TransactionRevertedError} if finalization fails.
|
|
2944
|
+
*
|
|
2945
|
+
* @example
|
|
2946
|
+
* ```ts
|
|
2947
|
+
* const txHash = await token.resumeUnshield(previousUnwrapTxHash);
|
|
2948
|
+
* ```
|
|
2949
|
+
*/
|
|
2950
|
+
async resumeUnshield(unwrapTxHash, callbacks) {
|
|
2951
|
+
return this.#waitAndFinalizeUnshield(unwrapTxHash, callbacks, crypto.randomUUID());
|
|
2952
|
+
}
|
|
2953
|
+
/**
|
|
2954
|
+
* Complete an unwrap by providing the public decryption proof.
|
|
2955
|
+
* Call this after an unshield request has been processed on-chain.
|
|
2956
|
+
*
|
|
2957
|
+
* @param burnAmountHandle - The encrypted amount handle from the `UnwrapRequested` event.
|
|
2958
|
+
* @returns The transaction hash and mined receipt.
|
|
2959
|
+
* @throws {@link DecryptionFailedError} if public decryption fails.
|
|
2960
|
+
* @throws {@link TransactionRevertedError} if the finalize transaction reverts.
|
|
2961
|
+
*
|
|
2962
|
+
* @example
|
|
2963
|
+
* ```ts
|
|
2964
|
+
* const event = findUnwrapRequested(receipt.logs);
|
|
2965
|
+
* const txHash = await token.finalizeUnwrap(event.encryptedAmount);
|
|
2966
|
+
* ```
|
|
2967
|
+
*/
|
|
2968
|
+
async finalizeUnwrap(burnAmountHandle) {
|
|
2969
|
+
let clearValue;
|
|
2970
|
+
let decryptionProof;
|
|
2971
|
+
const t0 = Date.now();
|
|
2972
|
+
try {
|
|
2973
|
+
this.emit({ type: ZamaSDKEvents.DecryptStart });
|
|
2974
|
+
const result = await this.sdk.publicDecrypt([burnAmountHandle]);
|
|
2975
|
+
this.emit({ type: ZamaSDKEvents.DecryptEnd, durationMs: Date.now() - t0 });
|
|
2976
|
+
decryptionProof = result.decryptionProof;
|
|
2977
|
+
try {
|
|
2978
|
+
clearValue = BigInt(result.abiEncodedClearValues);
|
|
2979
|
+
} catch {
|
|
2980
|
+
throw new DecryptionFailedError(
|
|
2981
|
+
`Cannot parse decrypted value: ${result.abiEncodedClearValues}`
|
|
2982
|
+
);
|
|
2983
|
+
}
|
|
2984
|
+
} catch (error) {
|
|
2985
|
+
this.emit({
|
|
2986
|
+
type: ZamaSDKEvents.DecryptError,
|
|
2987
|
+
error: toError2(error),
|
|
2988
|
+
durationMs: Date.now() - t0
|
|
2989
|
+
});
|
|
2990
|
+
if (error instanceof ZamaError) throw error;
|
|
2991
|
+
throw new DecryptionFailedError("Failed to finalize unshield", {
|
|
2992
|
+
cause: error instanceof Error ? error : void 0
|
|
2993
|
+
});
|
|
2994
|
+
}
|
|
2995
|
+
try {
|
|
2996
|
+
const txHash = await this.signer.writeContract(
|
|
2997
|
+
finalizeUnwrapContract(this.wrapper, burnAmountHandle, clearValue, decryptionProof)
|
|
2998
|
+
);
|
|
2999
|
+
this.emit({ type: ZamaSDKEvents.FinalizeUnwrapSubmitted, txHash });
|
|
3000
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
3001
|
+
return { txHash, receipt };
|
|
3002
|
+
} catch (error) {
|
|
3003
|
+
this.emit({
|
|
3004
|
+
type: ZamaSDKEvents.TransactionError,
|
|
3005
|
+
operation: "finalizeUnwrap",
|
|
3006
|
+
error: toError2(error)
|
|
3007
|
+
});
|
|
3008
|
+
if (error instanceof ZamaError) throw error;
|
|
3009
|
+
throw new TransactionRevertedError("Failed to finalize unshield", {
|
|
3010
|
+
cause: error instanceof Error ? error : void 0
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
/**
|
|
3015
|
+
* Approve this token contract to spend the underlying ERC-20.
|
|
3016
|
+
* Defaults to max uint256. Resets to zero first if there's an existing
|
|
3017
|
+
* non-zero allowance (required by tokens like USDT).
|
|
3018
|
+
*
|
|
3019
|
+
* @param amount - Optional approval amount. Defaults to max uint256.
|
|
3020
|
+
* @returns The transaction hash and mined receipt.
|
|
3021
|
+
* @throws {@link ApprovalFailedError} if the approval transaction fails.
|
|
3022
|
+
*
|
|
3023
|
+
* @example
|
|
3024
|
+
* ```ts
|
|
3025
|
+
* await token.approveUnderlying(); // max approval
|
|
3026
|
+
* await token.approveUnderlying(1000n); // exact amount
|
|
3027
|
+
* ```
|
|
3028
|
+
*/
|
|
3029
|
+
async approveUnderlying(amount) {
|
|
3030
|
+
const underlying = await this.#getUnderlying();
|
|
3031
|
+
const approvalAmount = amount ?? 2n ** 256n - 1n;
|
|
3032
|
+
try {
|
|
3033
|
+
if (approvalAmount > 0n) {
|
|
3034
|
+
const userAddress = await this.signer.getAddress();
|
|
3035
|
+
const currentAllowance = await this.signer.readContract(
|
|
3036
|
+
allowanceContract(underlying, userAddress, this.wrapper)
|
|
3037
|
+
);
|
|
3038
|
+
if (currentAllowance > 0n) {
|
|
3039
|
+
await this.signer.writeContract(approveContract(underlying, this.wrapper, 0n));
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
const txHash = await this.signer.writeContract(
|
|
3043
|
+
approveContract(underlying, this.wrapper, approvalAmount)
|
|
3044
|
+
);
|
|
3045
|
+
this.emit({ type: ZamaSDKEvents.ApproveUnderlyingSubmitted, txHash });
|
|
3046
|
+
const receipt = await this.signer.waitForTransactionReceipt(txHash);
|
|
3047
|
+
return { txHash, receipt };
|
|
3048
|
+
} catch (error) {
|
|
3049
|
+
this.emit({
|
|
3050
|
+
type: ZamaSDKEvents.TransactionError,
|
|
3051
|
+
operation: "approveUnderlying",
|
|
3052
|
+
error: toError2(error)
|
|
3053
|
+
});
|
|
3054
|
+
if (error instanceof ZamaError) throw error;
|
|
3055
|
+
throw new ApprovalFailedError("ERC-20 approval failed", {
|
|
3056
|
+
cause: error instanceof Error ? error : void 0
|
|
3057
|
+
});
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
// PRIVATE HELPERS
|
|
3061
|
+
async #waitAndFinalizeUnshield(unshieldHash, callbacks, operationId) {
|
|
3062
|
+
this.emit({ type: ZamaSDKEvents.UnshieldPhase1Submitted, txHash: unshieldHash, operationId });
|
|
3063
|
+
let receipt;
|
|
3064
|
+
try {
|
|
3065
|
+
receipt = await this.signer.waitForTransactionReceipt(unshieldHash);
|
|
3066
|
+
} catch (error) {
|
|
3067
|
+
if (error instanceof ZamaError) throw error;
|
|
3068
|
+
throw new TransactionRevertedError("Failed to get unshield receipt", {
|
|
3069
|
+
cause: error
|
|
3070
|
+
});
|
|
3071
|
+
}
|
|
3072
|
+
const event = findUnwrapRequested(receipt.logs);
|
|
3073
|
+
if (!event) {
|
|
3074
|
+
throw new TransactionRevertedError("No UnwrapRequested event found in unshield receipt");
|
|
3075
|
+
}
|
|
3076
|
+
this.emit({ type: ZamaSDKEvents.UnshieldPhase2Started, operationId });
|
|
3077
|
+
safeCallback(() => callbacks?.onFinalizing?.());
|
|
3078
|
+
const finalizeResult = await this.finalizeUnwrap(event.encryptedAmount);
|
|
3079
|
+
this.emit({
|
|
3080
|
+
type: ZamaSDKEvents.UnshieldPhase2Submitted,
|
|
3081
|
+
txHash: finalizeResult.txHash,
|
|
3082
|
+
operationId
|
|
3083
|
+
});
|
|
3084
|
+
safeCallback(() => callbacks?.onFinalizeSubmitted?.(finalizeResult.txHash));
|
|
3085
|
+
return finalizeResult;
|
|
3086
|
+
}
|
|
3087
|
+
async #ensureAllowance(amount, maxApproval, callbacks) {
|
|
3088
|
+
const underlying = await this.#getUnderlying();
|
|
3089
|
+
const userAddress = await this.signer.getAddress();
|
|
3090
|
+
const allowance = await this.signer.readContract(
|
|
3091
|
+
allowanceContract(underlying, userAddress, this.wrapper)
|
|
3092
|
+
);
|
|
3093
|
+
if (allowance >= amount) return;
|
|
3094
|
+
try {
|
|
3095
|
+
if (allowance > 0n) {
|
|
3096
|
+
await this.signer.writeContract(approveContract(underlying, this.wrapper, 0n));
|
|
3097
|
+
}
|
|
3098
|
+
const approvalAmount = maxApproval ? 2n ** 256n - 1n : amount;
|
|
3099
|
+
const txHash = await this.signer.writeContract(
|
|
3100
|
+
approveContract(underlying, this.wrapper, approvalAmount)
|
|
3101
|
+
);
|
|
3102
|
+
this.emit({ type: ZamaSDKEvents.ApproveUnderlyingSubmitted, txHash });
|
|
3103
|
+
safeCallback(() => callbacks?.onApprovalSubmitted?.(txHash));
|
|
3104
|
+
} catch (error) {
|
|
3105
|
+
if (error instanceof ZamaError) throw error;
|
|
3106
|
+
throw new ApprovalFailedError("ERC-20 approval failed", {
|
|
3107
|
+
cause: error instanceof Error ? error : void 0
|
|
3108
|
+
});
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
};
|
|
3112
|
+
function safeCallback(fn) {
|
|
3113
|
+
try {
|
|
3114
|
+
fn();
|
|
3115
|
+
} catch {
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
|
|
3119
|
+
// src/token/zama-sdk.ts
|
|
3120
|
+
var ZamaSDK = class {
|
|
3121
|
+
relayer;
|
|
3122
|
+
signer;
|
|
3123
|
+
storage;
|
|
3124
|
+
sessionStorage;
|
|
3125
|
+
credentials;
|
|
3126
|
+
#onEvent;
|
|
3127
|
+
#unsubscribeSigner;
|
|
3128
|
+
#identityReady;
|
|
3129
|
+
#lastAddress = null;
|
|
3130
|
+
#lastChainId = null;
|
|
3131
|
+
constructor(config) {
|
|
3132
|
+
this.relayer = config.relayer;
|
|
3133
|
+
this.signer = config.signer;
|
|
3134
|
+
this.storage = config.storage;
|
|
3135
|
+
this.sessionStorage = config.sessionStorage ?? new MemoryStorage();
|
|
3136
|
+
this.#onEvent = config.onEvent ?? function() {
|
|
3137
|
+
};
|
|
3138
|
+
this.credentials = new CredentialsManager({
|
|
3139
|
+
relayer: this.relayer,
|
|
3140
|
+
signer: this.signer,
|
|
3141
|
+
storage: this.storage,
|
|
3142
|
+
sessionStorage: this.sessionStorage,
|
|
3143
|
+
durationDays: config.credentialDurationDays ?? 1,
|
|
3144
|
+
onEvent: this.#onEvent
|
|
3145
|
+
});
|
|
3146
|
+
this.#identityReady = this.#initIdentity();
|
|
3147
|
+
if (this.signer.subscribe) {
|
|
3148
|
+
this.#unsubscribeSigner = this.signer.subscribe({
|
|
3149
|
+
onDisconnect: () => {
|
|
3150
|
+
void this.#revokeByTrackedIdentity().then(() => {
|
|
3151
|
+
this.#lastAddress = null;
|
|
3152
|
+
this.#lastChainId = null;
|
|
3153
|
+
});
|
|
3154
|
+
},
|
|
3155
|
+
onAccountChange: (newAddress) => {
|
|
3156
|
+
void this.#revokeByTrackedIdentity().then(async () => {
|
|
3157
|
+
this.#lastAddress = newAddress.toLowerCase();
|
|
3158
|
+
try {
|
|
3159
|
+
this.#lastChainId = await this.signer.getChainId();
|
|
3160
|
+
} catch {
|
|
3161
|
+
}
|
|
3162
|
+
});
|
|
3163
|
+
}
|
|
3164
|
+
});
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
async #initIdentity() {
|
|
3168
|
+
try {
|
|
3169
|
+
const address = (await this.signer.getAddress()).toLowerCase();
|
|
3170
|
+
const chainId = await this.signer.getChainId();
|
|
3171
|
+
this.#lastAddress = address;
|
|
3172
|
+
this.#lastChainId = chainId;
|
|
3173
|
+
} catch {
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
async #revokeByTrackedIdentity() {
|
|
3177
|
+
await this.#identityReady;
|
|
3178
|
+
if (this.#lastAddress == null || this.#lastChainId == null) return;
|
|
3179
|
+
const storeKey = await computeStoreKey(this.#lastAddress, this.#lastChainId);
|
|
3180
|
+
await this.sessionStorage.delete(storeKey);
|
|
3181
|
+
this.#onEvent?.({
|
|
3182
|
+
type: ZamaSDKEvents.CredentialsRevoked,
|
|
3183
|
+
timestamp: Date.now()
|
|
3184
|
+
});
|
|
3185
|
+
}
|
|
3186
|
+
/**
|
|
3187
|
+
* Create a read-only interface for a confidential token.
|
|
3188
|
+
* Supports balance queries and authorization without a wrapper address.
|
|
3189
|
+
*
|
|
3190
|
+
* @param address - The confidential token contract address.
|
|
3191
|
+
* @returns A {@link ReadonlyToken} instance bound to this SDK's relayer, signer, and storage.
|
|
3192
|
+
*/
|
|
3193
|
+
createReadonlyToken(address) {
|
|
3194
|
+
return new ReadonlyToken({
|
|
3195
|
+
relayer: this.relayer,
|
|
3196
|
+
signer: this.signer,
|
|
3197
|
+
storage: this.storage,
|
|
3198
|
+
sessionStorage: this.sessionStorage,
|
|
3199
|
+
credentials: this.credentials,
|
|
3200
|
+
address: normalizeAddress(address, "address"),
|
|
3201
|
+
onEvent: this.#onEvent
|
|
3202
|
+
});
|
|
3203
|
+
}
|
|
3204
|
+
/**
|
|
3205
|
+
* Create a high-level ERC-20-like interface for a confidential token.
|
|
3206
|
+
* Includes write operations (transfer, shield, unshield).
|
|
3207
|
+
*
|
|
3208
|
+
* @param address - The confidential token contract address (also used as wrapper by default).
|
|
3209
|
+
* @param wrapper - Optional explicit wrapper address, if it differs from the token address.
|
|
3210
|
+
* @returns A {@link Token} instance bound to this SDK's relayer, signer, and storage.
|
|
3211
|
+
*/
|
|
3212
|
+
createToken(address, wrapper) {
|
|
3213
|
+
return new Token({
|
|
3214
|
+
relayer: this.relayer,
|
|
3215
|
+
signer: this.signer,
|
|
3216
|
+
storage: this.storage,
|
|
3217
|
+
sessionStorage: this.sessionStorage,
|
|
3218
|
+
credentials: this.credentials,
|
|
3219
|
+
address: normalizeAddress(address, "address"),
|
|
3220
|
+
wrapper: wrapper ? normalizeAddress(wrapper, "wrapper") : void 0,
|
|
3221
|
+
onEvent: this.#onEvent
|
|
3222
|
+
});
|
|
3223
|
+
}
|
|
3224
|
+
/**
|
|
3225
|
+
* Pre-authorize FHE credentials for one or more contract addresses.
|
|
3226
|
+
* A single wallet signature covers all addresses, so subsequent decrypt
|
|
3227
|
+
* operations on any of these tokens reuse cached credentials.
|
|
3228
|
+
*
|
|
3229
|
+
* @param contractAddresses - Token contract addresses to authorize.
|
|
3230
|
+
*
|
|
3231
|
+
* @example
|
|
3232
|
+
* ```ts
|
|
3233
|
+
* await sdk.allow("0xTokenA", "0xTokenB");
|
|
3234
|
+
* ```
|
|
3235
|
+
*/
|
|
3236
|
+
async allow(...contractAddresses) {
|
|
3237
|
+
await this.credentials.allow(...contractAddresses);
|
|
3238
|
+
}
|
|
3239
|
+
/**
|
|
3240
|
+
* Revoke the session signature for the current signer.
|
|
3241
|
+
* The next decrypt operation will require a fresh wallet signature.
|
|
3242
|
+
*
|
|
3243
|
+
* @param contractAddresses - Optional addresses included in the
|
|
3244
|
+
* `credentials:revoked` event for observability.
|
|
3245
|
+
*
|
|
3246
|
+
* @example
|
|
3247
|
+
* ```ts
|
|
3248
|
+
* wallet.on("disconnect", () => sdk.revoke());
|
|
3249
|
+
* await sdk.revoke("0xTokenA", "0xTokenB");
|
|
3250
|
+
* ```
|
|
3251
|
+
*/
|
|
3252
|
+
async revoke(...contractAddresses) {
|
|
3253
|
+
await this.credentials.revoke(...contractAddresses);
|
|
3254
|
+
}
|
|
3255
|
+
/**
|
|
3256
|
+
* Revoke the session signature for the current signer without requiring
|
|
3257
|
+
* contract addresses. Uses the tracked identity when available (safe during
|
|
3258
|
+
* account switches), falling back to querying the signer directly.
|
|
3259
|
+
*
|
|
3260
|
+
* @example
|
|
3261
|
+
* ```ts
|
|
3262
|
+
* wallet.on("disconnect", () => sdk.revokeSession());
|
|
3263
|
+
* ```
|
|
3264
|
+
*/
|
|
3265
|
+
async revokeSession() {
|
|
3266
|
+
await this.#identityReady;
|
|
3267
|
+
const address = this.#lastAddress ?? (await this.signer.getAddress()).toLowerCase();
|
|
3268
|
+
const chainId = this.#lastChainId ?? await this.signer.getChainId();
|
|
3269
|
+
const storeKey = await computeStoreKey(address, chainId);
|
|
3270
|
+
await this.sessionStorage.delete(storeKey);
|
|
3271
|
+
this.#onEvent?.({
|
|
3272
|
+
type: ZamaSDKEvents.CredentialsRevoked,
|
|
3273
|
+
timestamp: Date.now()
|
|
3274
|
+
});
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Whether a session signature is currently cached for the connected wallet.
|
|
3278
|
+
* Use this to check if decrypt operations can proceed without a wallet prompt.
|
|
3279
|
+
*/
|
|
3280
|
+
async isAllowed() {
|
|
3281
|
+
return this.credentials.isAllowed();
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* Unsubscribe from signer lifecycle events without terminating the relayer.
|
|
3285
|
+
* Call this when the SDK instance is being replaced but the relayer is shared
|
|
3286
|
+
* (e.g. React provider remount in Strict Mode).
|
|
3287
|
+
*/
|
|
3288
|
+
dispose() {
|
|
3289
|
+
this.#unsubscribeSigner?.();
|
|
3290
|
+
this.#unsubscribeSigner = void 0;
|
|
3291
|
+
}
|
|
3292
|
+
/**
|
|
3293
|
+
* Terminate the relayer backend and clean up resources.
|
|
3294
|
+
* Call this when the SDK is no longer needed (e.g. on unmount or shutdown).
|
|
3295
|
+
*/
|
|
3296
|
+
terminate() {
|
|
3297
|
+
this.dispose();
|
|
3298
|
+
this.relayer.terminate();
|
|
3299
|
+
}
|
|
3300
|
+
};
|
|
3301
|
+
|
|
3302
|
+
// src/token/indexeddb-storage.ts
|
|
3303
|
+
var IndexedDBStorage = class {
|
|
3304
|
+
#db = null;
|
|
3305
|
+
#dbPromise = null;
|
|
3306
|
+
#dbName;
|
|
3307
|
+
#dbVersion;
|
|
3308
|
+
#storeName = "credentials";
|
|
3309
|
+
constructor(dbName = "CredentialStore", dbVersion = 1) {
|
|
3310
|
+
this.#dbName = dbName;
|
|
3311
|
+
this.#dbVersion = dbVersion;
|
|
3312
|
+
}
|
|
3313
|
+
#getDB() {
|
|
3314
|
+
if (this.#db) return Promise.resolve(this.#db);
|
|
3315
|
+
if (this.#dbPromise) return this.#dbPromise;
|
|
3316
|
+
this.#dbPromise = new Promise((resolve, reject) => {
|
|
3317
|
+
const request = indexedDB.open(this.#dbName, this.#dbVersion);
|
|
3318
|
+
request.onupgradeneeded = () => {
|
|
3319
|
+
const db = request.result;
|
|
3320
|
+
if (!db.objectStoreNames.contains(this.#storeName)) {
|
|
3321
|
+
db.createObjectStore(this.#storeName, { keyPath: "key" });
|
|
3322
|
+
}
|
|
3323
|
+
};
|
|
3324
|
+
request.onsuccess = () => {
|
|
3325
|
+
this.#db = request.result;
|
|
3326
|
+
this.#dbPromise = null;
|
|
3327
|
+
this.#db.onversionchange = () => {
|
|
3328
|
+
this.#db?.close();
|
|
3329
|
+
this.#db = null;
|
|
3330
|
+
this.#dbPromise = null;
|
|
3331
|
+
};
|
|
3332
|
+
this.#db.onclose = () => {
|
|
3333
|
+
this.#db = null;
|
|
3334
|
+
this.#dbPromise = null;
|
|
3335
|
+
};
|
|
3336
|
+
resolve(this.#db);
|
|
3337
|
+
};
|
|
3338
|
+
request.onerror = () => {
|
|
3339
|
+
this.#db = null;
|
|
3340
|
+
this.#dbPromise = null;
|
|
3341
|
+
reject(request.error);
|
|
3342
|
+
};
|
|
3343
|
+
});
|
|
3344
|
+
return this.#dbPromise;
|
|
3345
|
+
}
|
|
3346
|
+
async get(key) {
|
|
3347
|
+
const db = await this.#getDB();
|
|
3348
|
+
return new Promise((resolve, reject) => {
|
|
3349
|
+
const tx = db.transaction(this.#storeName, "readonly");
|
|
3350
|
+
tx.onabort = () => reject(tx.error ?? new Error("Transaction aborted"));
|
|
3351
|
+
const store = tx.objectStore(this.#storeName);
|
|
3352
|
+
const request = store.get(key);
|
|
3353
|
+
request.onsuccess = () => resolve(request.result?.value ?? null);
|
|
3354
|
+
request.onerror = () => reject(request.error);
|
|
3355
|
+
});
|
|
3356
|
+
}
|
|
3357
|
+
async set(key, value) {
|
|
3358
|
+
const db = await this.#getDB();
|
|
3359
|
+
return new Promise((resolve, reject) => {
|
|
3360
|
+
const tx = db.transaction(this.#storeName, "readwrite");
|
|
3361
|
+
tx.onabort = () => reject(tx.error ?? new Error("Transaction aborted"));
|
|
3362
|
+
const store = tx.objectStore(this.#storeName);
|
|
3363
|
+
const request = store.put({ key, value });
|
|
3364
|
+
request.onsuccess = () => resolve();
|
|
3365
|
+
request.onerror = () => reject(request.error);
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
async delete(key) {
|
|
3369
|
+
const db = await this.#getDB();
|
|
3370
|
+
return new Promise((resolve, reject) => {
|
|
3371
|
+
const tx = db.transaction(this.#storeName, "readwrite");
|
|
3372
|
+
tx.onabort = () => reject(tx.error ?? new Error("Transaction aborted"));
|
|
3373
|
+
const store = tx.objectStore(this.#storeName);
|
|
3374
|
+
const request = store.delete(key);
|
|
3375
|
+
request.onsuccess = () => resolve();
|
|
3376
|
+
request.onerror = () => reject(request.error);
|
|
3377
|
+
});
|
|
3378
|
+
}
|
|
3379
|
+
async clear() {
|
|
3380
|
+
const db = await this.#getDB();
|
|
3381
|
+
return new Promise((resolve, reject) => {
|
|
3382
|
+
const tx = db.transaction(this.#storeName, "readwrite");
|
|
3383
|
+
tx.onabort = () => reject(tx.error ?? new Error("Transaction aborted"));
|
|
3384
|
+
const store = tx.objectStore(this.#storeName);
|
|
3385
|
+
const request = store.clear();
|
|
3386
|
+
request.onsuccess = () => resolve();
|
|
3387
|
+
request.onerror = () => reject(request.error);
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
};
|
|
3391
|
+
var indexedDBStorage = new IndexedDBStorage();
|
|
3392
|
+
|
|
3393
|
+
// src/token/chrome-session-storage.ts
|
|
3394
|
+
var ChromeSessionStorage = class {
|
|
3395
|
+
async get(key) {
|
|
3396
|
+
const result = await chrome.storage.session.get(key);
|
|
3397
|
+
return result[key] ?? null;
|
|
3398
|
+
}
|
|
3399
|
+
async set(key, value) {
|
|
3400
|
+
await chrome.storage.session.set({ [key]: value });
|
|
3401
|
+
}
|
|
3402
|
+
async delete(key) {
|
|
3403
|
+
await chrome.storage.session.remove(key);
|
|
3404
|
+
}
|
|
3405
|
+
};
|
|
3406
|
+
var chromeSessionStorage = new ChromeSessionStorage();
|
|
3407
|
+
|
|
3408
|
+
// src/token/pending-unshield.ts
|
|
3409
|
+
var STORAGE_PREFIX = "zama:pending-unshield:";
|
|
3410
|
+
function storageKey2(wrapperAddress) {
|
|
3411
|
+
return `${STORAGE_PREFIX}${wrapperAddress.toLowerCase()}`;
|
|
3412
|
+
}
|
|
3413
|
+
async function savePendingUnshield(storage, wrapperAddress, unwrapTxHash) {
|
|
3414
|
+
await storage.set(storageKey2(wrapperAddress), unwrapTxHash);
|
|
3415
|
+
}
|
|
3416
|
+
async function loadPendingUnshield(storage, wrapperAddress) {
|
|
3417
|
+
return await storage.get(storageKey2(wrapperAddress));
|
|
3418
|
+
}
|
|
3419
|
+
async function clearPendingUnshield(storage, wrapperAddress) {
|
|
3420
|
+
await storage.delete(storageKey2(wrapperAddress));
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
// src/activity.ts
|
|
3424
|
+
function addressesEqual(a, b) {
|
|
3425
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
3426
|
+
}
|
|
3427
|
+
function classifyDirection(userAddress, from, to) {
|
|
3428
|
+
const isFrom = from !== void 0 && addressesEqual(userAddress, from);
|
|
3429
|
+
const isTo = to !== void 0 && addressesEqual(userAddress, to);
|
|
3430
|
+
if (isFrom && isTo) return "self";
|
|
3431
|
+
if (isFrom) return "outgoing";
|
|
3432
|
+
return "incoming";
|
|
3433
|
+
}
|
|
3434
|
+
function eventToActivityItem(event, userAddress, metadata) {
|
|
3435
|
+
switch (event.eventName) {
|
|
3436
|
+
case "ConfidentialTransfer":
|
|
3437
|
+
return buildTransfer(event, userAddress, metadata);
|
|
3438
|
+
case "Wrapped":
|
|
3439
|
+
return buildShield(event, userAddress, metadata);
|
|
3440
|
+
case "UnwrapRequested":
|
|
3441
|
+
return buildUnshieldRequested(event, userAddress, metadata);
|
|
3442
|
+
case "UnwrappedStarted":
|
|
3443
|
+
return buildUnshieldStarted(event, userAddress, metadata);
|
|
3444
|
+
case "UnwrappedFinalized":
|
|
3445
|
+
return buildUnshieldFinalized(event, metadata);
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
function buildTransfer(event, userAddress, metadata) {
|
|
3449
|
+
return {
|
|
3450
|
+
type: "transfer",
|
|
3451
|
+
direction: classifyDirection(userAddress, event.from, event.to),
|
|
3452
|
+
amount: { type: "encrypted", handle: event.encryptedAmountHandle },
|
|
3453
|
+
from: event.from,
|
|
3454
|
+
to: event.to,
|
|
3455
|
+
metadata,
|
|
3456
|
+
rawEvent: event
|
|
3457
|
+
};
|
|
3458
|
+
}
|
|
3459
|
+
function buildShield(event, userAddress, metadata) {
|
|
3460
|
+
return {
|
|
3461
|
+
type: "shield",
|
|
3462
|
+
direction: classifyDirection(userAddress, void 0, event.to),
|
|
3463
|
+
amount: { type: "clear", value: event.amountIn },
|
|
3464
|
+
to: event.to,
|
|
3465
|
+
fee: event.feeAmount,
|
|
3466
|
+
metadata,
|
|
3467
|
+
rawEvent: event
|
|
3468
|
+
};
|
|
3469
|
+
}
|
|
3470
|
+
function buildUnshieldRequested(event, userAddress, metadata) {
|
|
3471
|
+
return {
|
|
3472
|
+
type: "unshield_requested",
|
|
3473
|
+
direction: classifyDirection(userAddress, void 0, event.receiver),
|
|
3474
|
+
amount: { type: "encrypted", handle: event.encryptedAmount },
|
|
3475
|
+
to: event.receiver,
|
|
3476
|
+
metadata,
|
|
3477
|
+
rawEvent: event
|
|
3478
|
+
};
|
|
3479
|
+
}
|
|
3480
|
+
function buildUnshieldStarted(event, userAddress, metadata) {
|
|
3481
|
+
return {
|
|
3482
|
+
type: "unshield_started",
|
|
3483
|
+
direction: classifyDirection(userAddress, void 0, event.to),
|
|
3484
|
+
amount: { type: "encrypted", handle: event.requestedAmount },
|
|
3485
|
+
to: event.to,
|
|
3486
|
+
success: event.returnVal,
|
|
3487
|
+
metadata,
|
|
3488
|
+
rawEvent: event
|
|
3489
|
+
};
|
|
3490
|
+
}
|
|
3491
|
+
function buildUnshieldFinalized(event, metadata) {
|
|
3492
|
+
return {
|
|
3493
|
+
type: "unshield_finalized",
|
|
3494
|
+
// Finalized events don't carry from/to addresses, always treat as incoming
|
|
3495
|
+
direction: "incoming",
|
|
3496
|
+
amount: { type: "clear", value: event.unwrapAmount },
|
|
3497
|
+
fee: event.feeAmount,
|
|
3498
|
+
success: event.finalizeSuccess,
|
|
3499
|
+
metadata,
|
|
3500
|
+
rawEvent: event
|
|
3501
|
+
};
|
|
3502
|
+
}
|
|
3503
|
+
function parseActivityFeed(logs, userAddress) {
|
|
3504
|
+
const items = [];
|
|
3505
|
+
for (const log of logs) {
|
|
3506
|
+
const event = decodeOnChainEvent(log);
|
|
3507
|
+
if (!event) continue;
|
|
3508
|
+
const metadata = {
|
|
3509
|
+
transactionHash: log.transactionHash,
|
|
3510
|
+
blockNumber: log.blockNumber,
|
|
3511
|
+
logIndex: log.logIndex
|
|
3512
|
+
};
|
|
3513
|
+
items.push(eventToActivityItem(event, userAddress, metadata));
|
|
3514
|
+
}
|
|
3515
|
+
return items;
|
|
3516
|
+
}
|
|
3517
|
+
function extractEncryptedHandles(items) {
|
|
3518
|
+
const handles = /* @__PURE__ */ new Set();
|
|
3519
|
+
for (const item of items) {
|
|
3520
|
+
if (item.amount.type === "encrypted" && item.amount.decryptedValue === void 0) {
|
|
3521
|
+
const h = item.amount.handle;
|
|
3522
|
+
if (h !== "0x" && h !== ZERO_HANDLE) {
|
|
3523
|
+
handles.add(h);
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
}
|
|
3527
|
+
return [...handles];
|
|
3528
|
+
}
|
|
3529
|
+
function applyDecryptedValues(items, decryptedMap) {
|
|
3530
|
+
return items.map((item) => {
|
|
3531
|
+
if (item.amount.type !== "encrypted") return item;
|
|
3532
|
+
const value = decryptedMap.get(item.amount.handle);
|
|
3533
|
+
if (value === void 0) return item;
|
|
3534
|
+
return {
|
|
3535
|
+
...item,
|
|
3536
|
+
amount: {
|
|
3537
|
+
type: "encrypted",
|
|
3538
|
+
handle: item.amount.handle,
|
|
3539
|
+
decryptedValue: value
|
|
3540
|
+
}
|
|
3541
|
+
};
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
function sortByBlockNumber(items) {
|
|
3545
|
+
return [...items].sort((a, b) => {
|
|
3546
|
+
const aBlock = a.metadata.blockNumber;
|
|
3547
|
+
const bBlock = b.metadata.blockNumber;
|
|
3548
|
+
if (aBlock === void 0 && bBlock === void 0) return 0;
|
|
3549
|
+
if (aBlock === void 0) return -1;
|
|
3550
|
+
if (bBlock === void 0) return 1;
|
|
3551
|
+
const aBig = typeof aBlock === "bigint" ? aBlock : BigInt(aBlock);
|
|
3552
|
+
const bBig = typeof bBlock === "bigint" ? bBlock : BigInt(bBlock);
|
|
3553
|
+
if (bBig > aBig) return 1;
|
|
3554
|
+
if (bBig < aBig) return -1;
|
|
3555
|
+
const aIdx = a.metadata.logIndex ?? 0;
|
|
3556
|
+
const bIdx = b.metadata.logIndex ?? 0;
|
|
3557
|
+
return bIdx - aIdx;
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
export { BATCH_SWAP_ABI, ChromeSessionStorage, CredentialsManager, IndexedDBStorage, MemoryStorage, ReadonlyToken, RelayerWeb, TOKEN_TOPICS, Token, Topics, ZERO_HANDLE, ZamaSDK, ZamaSDKEvents, applyDecryptedValues, chromeSessionStorage, clearPendingUnshield, decodeConfidentialTransfer, decodeOnChainEvent, decodeOnChainEvents, decodeUnwrapRequested, decodeUnwrappedFinalized, decodeUnwrappedStarted, decodeWrapped, extractEncryptedHandles, findUnwrapRequested, findWrapped, indexedDBStorage, loadPendingUnshield, memoryStorage, parseActivityFeed, savePendingUnshield, sortByBlockNumber };
|
|
3562
|
+
//# sourceMappingURL=index.js.map
|
|
3563
|
+
//# sourceMappingURL=index.js.map
|