@kohaku-eth/tornado-cash 0.0.2-alpha.16 → 0.0.2-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +13 -0
- package/dist/index.js +2 -1
- package/dist/state-manager.worker.js +171 -74
- package/dist/state-manager.worker.node.js +171 -74
- package/dist/worker-loader.browser.d.ts +6 -0
- package/dist/worker-loader.node.d.ts +6 -0
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -502,6 +502,12 @@ interface IPaymasterWithdrawParams extends IWithdrawBaseParams {
|
|
|
502
502
|
mode: 'paymaster';
|
|
503
503
|
delegation?: DelegationConfig;
|
|
504
504
|
tailCalls?: (address: AccountId) => Promise<TxData[]>;
|
|
505
|
+
/**
|
|
506
|
+
* Optional baseline for the execution-phase gas of user `tailCalls` (not
|
|
507
|
+
* including per-note direct withdraws). Used when bundler estimation fails;
|
|
508
|
+
* when estimation succeeds the bundler estimate still wins.
|
|
509
|
+
*/
|
|
510
|
+
tailCallsGasEstimate?: bigint;
|
|
505
511
|
}
|
|
506
512
|
type IWithdrawapOperationParams = IRelayerWithdrawParams | IPaymasterWithdrawParams;
|
|
507
513
|
interface IGetNotesParams extends IBaseOperationParams {
|
|
@@ -567,6 +573,13 @@ interface TCRelayerUnshieldOptions extends UnshieldOptions {
|
|
|
567
573
|
interface TCPaymasterUnshieldOptions extends UnshieldOptions {
|
|
568
574
|
mode: 'paymaster';
|
|
569
575
|
delegation?: DelegationConfig;
|
|
576
|
+
/**
|
|
577
|
+
* Optional baseline for the execution-phase gas of user `tailCalls` (not
|
|
578
|
+
* including per-note direct withdraws). Used as the batch execution-tail
|
|
579
|
+
* baseline when bundler estimation fails; when estimation succeeds the
|
|
580
|
+
* bundler estimate still wins. Defaults to the SDK's static callGasLimit.
|
|
581
|
+
*/
|
|
582
|
+
tailCallsGasEstimate?: bigint;
|
|
570
583
|
}
|
|
571
584
|
type TCPrepareUnshieldOptions = TCRelayerUnshieldOptions | TCPaymasterUnshieldOptions;
|
|
572
585
|
interface TCPrepareShieldOptions {
|
package/dist/index.js
CHANGED
|
@@ -592,7 +592,8 @@ var TornadoCashProtocol = class {
|
|
|
592
592
|
...baseParams,
|
|
593
593
|
mode: "paymaster",
|
|
594
594
|
delegation: options.delegation,
|
|
595
|
-
tailCalls: options.tailCalls
|
|
595
|
+
tailCalls: options.tailCalls,
|
|
596
|
+
tailCallsGasEstimate: options.tailCallsGasEstimate
|
|
596
597
|
});
|
|
597
598
|
} else {
|
|
598
599
|
withdrawals = await stateManager.getWithdrawalPayloads({
|
|
@@ -22194,6 +22194,15 @@ var withdrawThunk = createAsyncThunk("withdraw/executeWithdrawals", async ({
|
|
|
22194
22194
|
return getWithdrawalsProofs();
|
|
22195
22195
|
});
|
|
22196
22196
|
|
|
22197
|
+
// ../provider/dist/index.js
|
|
22198
|
+
var createTx = (address, payload, value = BigInt(0)) => {
|
|
22199
|
+
return {
|
|
22200
|
+
to: address,
|
|
22201
|
+
data: payload,
|
|
22202
|
+
value
|
|
22203
|
+
};
|
|
22204
|
+
};
|
|
22205
|
+
|
|
22197
22206
|
// ../../node_modules/.pnpm/viem@2.48.4_typescript@5.9.3_zod@4.4.3/node_modules/viem/_esm/index.js
|
|
22198
22207
|
init_exports();
|
|
22199
22208
|
init_encodeAbiParameters2();
|
|
@@ -22368,14 +22377,20 @@ function privateKeyToAccount(privateKey, options = {}) {
|
|
|
22368
22377
|
};
|
|
22369
22378
|
}
|
|
22370
22379
|
|
|
22380
|
+
// src/state/thunks/paymasterWithdrawThunk.ts
|
|
22381
|
+
init_esm();
|
|
22382
|
+
|
|
22371
22383
|
// src/paymaster/fee.ts
|
|
22372
22384
|
var ERC20_TRANSFER_GAS = 100000n;
|
|
22385
|
+
var PER_DIRECT_WITHDRAW_GAS = 400000n;
|
|
22386
|
+
var PER_DIRECT_WITHDRAW_GAS_ERC20 = 500000n;
|
|
22387
|
+
var FORWARD_GAS = 60000n;
|
|
22373
22388
|
var baseGasUnits = {
|
|
22374
|
-
preVerificationGas:
|
|
22389
|
+
preVerificationGas: 85000n,
|
|
22375
22390
|
verificationGasLimit: 50000n,
|
|
22376
22391
|
callGasLimit: 300000n,
|
|
22377
22392
|
paymasterVerificationGasLimit: 350000n,
|
|
22378
|
-
paymasterPostOpGasLimit:
|
|
22393
|
+
paymasterPostOpGasLimit: 50000n
|
|
22379
22394
|
};
|
|
22380
22395
|
function reasonableGasUnits(isERC20) {
|
|
22381
22396
|
if (!isERC20) return baseGasUnits;
|
|
@@ -22384,6 +22399,15 @@ function reasonableGasUnits(isERC20) {
|
|
|
22384
22399
|
paymasterVerificationGasLimit: baseGasUnits.paymasterVerificationGasLimit + ERC20_TRANSFER_GAS
|
|
22385
22400
|
};
|
|
22386
22401
|
}
|
|
22402
|
+
function reasonableGasUnitsForBatch(isERC20, extraWithdrawals, hasUserTailCalls, tailCallsGasEstimate) {
|
|
22403
|
+
const base = reasonableGasUnits(isERC20);
|
|
22404
|
+
const perWithdraw = isERC20 ? PER_DIRECT_WITHDRAW_GAS_ERC20 : PER_DIRECT_WITHDRAW_GAS;
|
|
22405
|
+
const executionTail = hasUserTailCalls ? tailCallsGasEstimate ?? base.callGasLimit : FORWARD_GAS;
|
|
22406
|
+
return {
|
|
22407
|
+
...base,
|
|
22408
|
+
callGasLimit: BigInt(extraWithdrawals) * perWithdraw + executionTail
|
|
22409
|
+
};
|
|
22410
|
+
}
|
|
22387
22411
|
function computeMinimumViableFee(reasonableGasUnits2, maxFeePerGas) {
|
|
22388
22412
|
const baseFeeMultiplier = 1.2;
|
|
22389
22413
|
const decimals = baseFeeMultiplier.toString().split(".")[1]?.length ?? 0;
|
|
@@ -22391,10 +22415,6 @@ function computeMinimumViableFee(reasonableGasUnits2, maxFeePerGas) {
|
|
|
22391
22415
|
const multiply = (base) => base * BigInt(Math.ceil(baseFeeMultiplier * denominator)) / BigInt(denominator);
|
|
22392
22416
|
const requiredGas = reasonableGasUnits2.verificationGasLimit + reasonableGasUnits2.callGasLimit + reasonableGasUnits2.paymasterVerificationGasLimit + reasonableGasUnits2.preVerificationGas + reasonableGasUnits2.paymasterPostOpGasLimit;
|
|
22393
22417
|
const requiredPrefund = requiredGas * maxFeePerGas;
|
|
22394
|
-
console.log("totalGasUnits", requiredGas);
|
|
22395
|
-
console.log("maxFeePerGas", maxFeePerGas);
|
|
22396
|
-
console.log("requiredPrefund", requiredPrefund);
|
|
22397
|
-
console.log("multiply", 1.2);
|
|
22398
22418
|
return multiply(requiredPrefund);
|
|
22399
22419
|
}
|
|
22400
22420
|
|
|
@@ -24091,6 +24111,19 @@ async function getUserOperationGasPrice(client) {
|
|
|
24091
24111
|
fast: parse(result.fast)
|
|
24092
24112
|
};
|
|
24093
24113
|
}
|
|
24114
|
+
async function estimateUserOperationGas2(client, op, entryPoint) {
|
|
24115
|
+
const result = await client.request({
|
|
24116
|
+
method: "eth_estimateUserOperationGas",
|
|
24117
|
+
params: [op, entryPoint]
|
|
24118
|
+
});
|
|
24119
|
+
return {
|
|
24120
|
+
callGasLimit: BigInt(result.callGasLimit),
|
|
24121
|
+
verificationGasLimit: BigInt(result.verificationGasLimit),
|
|
24122
|
+
preVerificationGas: BigInt(result.preVerificationGas),
|
|
24123
|
+
paymasterVerificationGasLimit: BigInt(result.paymasterVerificationGasLimit ?? 0),
|
|
24124
|
+
paymasterPostOpGasLimit: BigInt(result.paymasterPostOpGasLimit ?? 0)
|
|
24125
|
+
};
|
|
24126
|
+
}
|
|
24094
24127
|
async function buildSignedTornadoUserOp({
|
|
24095
24128
|
signer,
|
|
24096
24129
|
chainId,
|
|
@@ -24191,6 +24224,7 @@ var paymasterWithdrawThunk = createAsyncThunk("withdraw/executePaymasterWithdraw
|
|
|
24191
24224
|
},
|
|
24192
24225
|
secretManager,
|
|
24193
24226
|
tailCalls,
|
|
24227
|
+
tailCallsGasEstimate,
|
|
24194
24228
|
...rest
|
|
24195
24229
|
}, { getState, dispatch }) => {
|
|
24196
24230
|
const state = getState();
|
|
@@ -24221,12 +24255,32 @@ var paymasterWithdrawThunk = createAsyncThunk("withdraw/executePaymasterWithdraw
|
|
|
24221
24255
|
);
|
|
24222
24256
|
const bundlerClient = createPaymasterBundlerClient(bundlerUrl);
|
|
24223
24257
|
const { standard: { maxFeePerGas, maxPriorityFeePerGas } } = await getUserOperationGasPrice(bundlerClient);
|
|
24224
|
-
const gasUnits = reasonableGasUnits(poolInfo.isERC20);
|
|
24225
|
-
const ethFee = computeMinimumViableFee(gasUnits, maxFeePerGas);
|
|
24226
|
-
const fee = poolInfo.isERC20 ? await dataService.quoteWeiInToken(BigInt(paymasterAddress), poolInfo.asset, ethFee) : ethFee;
|
|
24227
24258
|
const relayerAddress = BigInt(paymasterAddress);
|
|
24228
24259
|
const bigintChainId = await dataService.getChainId();
|
|
24229
24260
|
const { recipient: originalRecipient, ...restWithoutRecipient } = rest;
|
|
24261
|
+
const quoteFee = async (ethFee) => poolInfo.isERC20 ? dataService.quoteWeiInToken(BigInt(paymasterAddress), poolInfo.asset, ethFee) : ethFee;
|
|
24262
|
+
const prove = async (deposit, recipient, relayer, depositFee) => unwrapResult(await dispatch(withdrawalsProofThunk({
|
|
24263
|
+
...restWithoutRecipient,
|
|
24264
|
+
recipient,
|
|
24265
|
+
deposit,
|
|
24266
|
+
relayerAddress: relayer,
|
|
24267
|
+
fee: depositFee
|
|
24268
|
+
})));
|
|
24269
|
+
const buildPaymasterData = (poolAddress, proof) => {
|
|
24270
|
+
const [root, nullifierHash, proofRecipient, relayerArg, feeArg, refundArg] = proof.args;
|
|
24271
|
+
return encodePaymasterData(
|
|
24272
|
+
poolAcountsMap.get(poolAddress),
|
|
24273
|
+
encodeTornadoAdapterData(
|
|
24274
|
+
proof.proof,
|
|
24275
|
+
root,
|
|
24276
|
+
nullifierHash,
|
|
24277
|
+
proofRecipient,
|
|
24278
|
+
relayerArg,
|
|
24279
|
+
BigInt(feeArg),
|
|
24280
|
+
BigInt(refundArg)
|
|
24281
|
+
)
|
|
24282
|
+
);
|
|
24283
|
+
};
|
|
24230
24284
|
const ephemeralSigner = async (deposit) => privateKeyToAccount(await secretManager.deriveEphemeralSigner({
|
|
24231
24285
|
depositIndex: deposit.index,
|
|
24232
24286
|
chainId: bigintChainId,
|
|
@@ -24241,78 +24295,120 @@ var paymasterWithdrawThunk = createAsyncThunk("withdraw/executePaymasterWithdraw
|
|
|
24241
24295
|
return ephemeralSigner(deposits[0]);
|
|
24242
24296
|
};
|
|
24243
24297
|
const resolveIndependentSigner = (deposit) => delegation?.mode === "deterministic" ? ephemeralSigner(deposit) : Promise.resolve(privateKeyToAccount(generatePrivateKey()));
|
|
24244
|
-
const
|
|
24245
|
-
|
|
24246
|
-
|
|
24247
|
-
|
|
24248
|
-
const
|
|
24249
|
-
const
|
|
24250
|
-
const
|
|
24251
|
-
const
|
|
24252
|
-
|
|
24253
|
-
|
|
24254
|
-
|
|
24255
|
-
|
|
24256
|
-
|
|
24257
|
-
|
|
24258
|
-
|
|
24259
|
-
|
|
24260
|
-
|
|
24261
|
-
|
|
24262
|
-
|
|
24263
|
-
|
|
24264
|
-
|
|
24265
|
-
|
|
24266
|
-
|
|
24267
|
-
|
|
24268
|
-
|
|
24269
|
-
|
|
24270
|
-
|
|
24271
|
-
|
|
24272
|
-
|
|
24273
|
-
|
|
24274
|
-
|
|
24275
|
-
|
|
24276
|
-
|
|
24277
|
-
|
|
24278
|
-
|
|
24279
|
-
|
|
24280
|
-
|
|
24281
|
-
|
|
24282
|
-
|
|
24283
|
-
|
|
24284
|
-
|
|
24285
|
-
|
|
24286
|
-
|
|
24287
|
-
maxPriorityFeePerGas,
|
|
24288
|
-
tailCalls: effectiveTailCalls,
|
|
24289
|
-
// When sharing one delegator across multiple deposits each userOp needs
|
|
24290
|
-
// a distinct nonce (0, 1, 2 …) on the shared sender.
|
|
24291
|
-
nonce: sharedDelegator ? BigInt(i) : 0n
|
|
24292
|
-
})
|
|
24293
|
-
);
|
|
24298
|
+
const needsConsolidation = deposits.length > 1 || tailCalls != null;
|
|
24299
|
+
if (!needsConsolidation) {
|
|
24300
|
+
const deposit = deposits[0];
|
|
24301
|
+
const signer2 = await resolveIndependentSigner(deposit);
|
|
24302
|
+
const gasUnits = reasonableGasUnits(poolInfo.isERC20);
|
|
24303
|
+
const fee2 = await quoteFee(computeMinimumViableFee(gasUnits, maxFeePerGas));
|
|
24304
|
+
const proof = await prove(deposit, originalRecipient, relayerAddress, fee2);
|
|
24305
|
+
const userOperation2 = await buildSignedTornadoUserOp({
|
|
24306
|
+
signer: signer2,
|
|
24307
|
+
chainId,
|
|
24308
|
+
paymasterAddress,
|
|
24309
|
+
paymasterData: buildPaymasterData(deposit.pool, proof),
|
|
24310
|
+
gas: { ...gasUnits, callGasLimit: 0n },
|
|
24311
|
+
maxFeePerGas,
|
|
24312
|
+
maxPriorityFeePerGas,
|
|
24313
|
+
nonce: 0n
|
|
24314
|
+
});
|
|
24315
|
+
return [{
|
|
24316
|
+
mode: "paymaster",
|
|
24317
|
+
proof,
|
|
24318
|
+
poolAddress: deposit.pool,
|
|
24319
|
+
isERC20: poolInfo.isERC20,
|
|
24320
|
+
paymasterAddress,
|
|
24321
|
+
entryPointAddress,
|
|
24322
|
+
bundlerUrl,
|
|
24323
|
+
userOperation: userOperation2
|
|
24324
|
+
}];
|
|
24325
|
+
}
|
|
24326
|
+
const signer = await resolveBatchDelegator();
|
|
24327
|
+
const senderAddress = BigInt(signer.address);
|
|
24328
|
+
const sponsoringDeposit = deposits[0];
|
|
24329
|
+
const directDeposits = deposits.slice(1);
|
|
24330
|
+
const ZERO_ADDRESS = 0n;
|
|
24331
|
+
const directWithdrawCalls = [];
|
|
24332
|
+
for (const deposit of directDeposits) {
|
|
24333
|
+
const proof = await prove(deposit, senderAddress, ZERO_ADDRESS, 0n);
|
|
24334
|
+
const [root, nullifierHash, proofRecipient, relayerArg, feeArg, refundArg] = proof.args;
|
|
24335
|
+
const data = encodeFunctionData({
|
|
24336
|
+
abi: poolAbi,
|
|
24337
|
+
functionName: "withdraw",
|
|
24338
|
+
args: [proof.proof, root, nullifierHash, proofRecipient, relayerArg, BigInt(feeArg), BigInt(refundArg)]
|
|
24339
|
+
});
|
|
24340
|
+
directWithdrawCalls.push(createTx(addressToHex(deposit.pool), data, 0n));
|
|
24294
24341
|
}
|
|
24295
|
-
|
|
24342
|
+
const totalDenomination = deposits.reduce(
|
|
24343
|
+
(sum, d) => sum + pools.get(d.pool).denomination,
|
|
24344
|
+
0n
|
|
24345
|
+
);
|
|
24346
|
+
const forwardCalls = (fee2) => {
|
|
24347
|
+
const amount2 = totalDenomination - fee2;
|
|
24348
|
+
if (poolInfo.isERC20) {
|
|
24349
|
+
const data = encodeFunctionData({
|
|
24350
|
+
abi: erc20Abi,
|
|
24351
|
+
functionName: "transfer",
|
|
24352
|
+
args: [addressToHex(originalRecipient), amount2]
|
|
24353
|
+
});
|
|
24354
|
+
return [createTx(addressToHex(poolInfo.asset), data, 0n)];
|
|
24355
|
+
}
|
|
24356
|
+
return [createTx(addressToHex(originalRecipient), "0x", amount2)];
|
|
24357
|
+
};
|
|
24358
|
+
const composeTailCalls = (fee2) => async (addr) => [
|
|
24359
|
+
...directWithdrawCalls,
|
|
24360
|
+
...tailCalls ? await tailCalls(addr) : forwardCalls(fee2)
|
|
24361
|
+
];
|
|
24362
|
+
const buildOp = (gas2, fee2, paymasterData) => buildSignedTornadoUserOp({
|
|
24363
|
+
signer,
|
|
24364
|
+
chainId,
|
|
24365
|
+
paymasterAddress,
|
|
24366
|
+
paymasterData,
|
|
24367
|
+
gas: gas2,
|
|
24368
|
+
maxFeePerGas,
|
|
24369
|
+
maxPriorityFeePerGas,
|
|
24370
|
+
tailCalls: composeTailCalls(fee2),
|
|
24371
|
+
nonce: 0n
|
|
24372
|
+
});
|
|
24373
|
+
const baselineGas = reasonableGasUnitsForBatch(
|
|
24374
|
+
poolInfo.isERC20,
|
|
24375
|
+
directDeposits.length,
|
|
24376
|
+
tailCalls != null,
|
|
24377
|
+
tailCallsGasEstimate
|
|
24378
|
+
);
|
|
24379
|
+
let fee = await quoteFee(computeMinimumViableFee(baselineGas, maxFeePerGas));
|
|
24380
|
+
let gas = baselineGas;
|
|
24381
|
+
let sponsoringProof = await prove(sponsoringDeposit, senderAddress, relayerAddress, fee);
|
|
24382
|
+
let userOperation = await buildOp(gas, fee, buildPaymasterData(sponsoringDeposit.pool, sponsoringProof));
|
|
24383
|
+
try {
|
|
24384
|
+
const estimated = await estimateUserOperationGas2(bundlerClient, userOperation, entryPointAddress);
|
|
24385
|
+
const buffer2 = (x) => x * 12n / 10n;
|
|
24386
|
+
const orBaseline = (e, b) => e > 0n ? buffer2(e) : b;
|
|
24387
|
+
const refinedGas = {
|
|
24388
|
+
callGasLimit: orBaseline(estimated.callGasLimit, baselineGas.callGasLimit),
|
|
24389
|
+
verificationGasLimit: orBaseline(estimated.verificationGasLimit, baselineGas.verificationGasLimit),
|
|
24390
|
+
preVerificationGas: orBaseline(estimated.preVerificationGas, baselineGas.preVerificationGas),
|
|
24391
|
+
paymasterVerificationGasLimit: orBaseline(estimated.paymasterVerificationGasLimit, baselineGas.paymasterVerificationGasLimit),
|
|
24392
|
+
paymasterPostOpGasLimit: orBaseline(estimated.paymasterPostOpGasLimit, baselineGas.paymasterPostOpGasLimit)
|
|
24393
|
+
};
|
|
24394
|
+
fee = await quoteFee(computeMinimumViableFee(refinedGas, maxFeePerGas));
|
|
24395
|
+
gas = refinedGas;
|
|
24396
|
+
sponsoringProof = await prove(sponsoringDeposit, senderAddress, relayerAddress, fee);
|
|
24397
|
+
userOperation = await buildOp(gas, fee, buildPaymasterData(sponsoringDeposit.pool, sponsoringProof));
|
|
24398
|
+
} catch {
|
|
24399
|
+
}
|
|
24400
|
+
return [{
|
|
24296
24401
|
mode: "paymaster",
|
|
24297
|
-
proof,
|
|
24298
|
-
poolAddress,
|
|
24402
|
+
proof: sponsoringProof,
|
|
24403
|
+
poolAddress: sponsoringDeposit.pool,
|
|
24299
24404
|
isERC20: poolInfo.isERC20,
|
|
24300
24405
|
paymasterAddress,
|
|
24301
24406
|
entryPointAddress,
|
|
24302
24407
|
bundlerUrl,
|
|
24303
|
-
userOperation
|
|
24304
|
-
}
|
|
24408
|
+
userOperation
|
|
24409
|
+
}];
|
|
24305
24410
|
});
|
|
24306
24411
|
|
|
24307
|
-
// ../provider/dist/index.js
|
|
24308
|
-
var createTx = (address, payload, value = BigInt(0)) => {
|
|
24309
|
-
return {
|
|
24310
|
-
to: address,
|
|
24311
|
-
data: payload,
|
|
24312
|
-
value
|
|
24313
|
-
};
|
|
24314
|
-
};
|
|
24315
|
-
|
|
24316
24412
|
// src/account/tx/shield.ts
|
|
24317
24413
|
init_esm();
|
|
24318
24414
|
init_esm();
|
|
@@ -24595,7 +24691,8 @@ var storeStateManager = async ({
|
|
|
24595
24691
|
delegation: params.delegation
|
|
24596
24692
|
},
|
|
24597
24693
|
secretManager,
|
|
24598
|
-
tailCalls: params.tailCalls
|
|
24694
|
+
tailCalls: params.tailCalls,
|
|
24695
|
+
tailCallsGasEstimate: params.tailCallsGasEstimate
|
|
24599
24696
|
})
|
|
24600
24697
|
)
|
|
24601
24698
|
);
|
|
@@ -21420,6 +21420,15 @@ var withdrawThunk = createAsyncThunk("withdraw/executeWithdrawals", async ({
|
|
|
21420
21420
|
return getWithdrawalsProofs();
|
|
21421
21421
|
});
|
|
21422
21422
|
|
|
21423
|
+
// ../provider/dist/index.js
|
|
21424
|
+
var createTx = (address, payload, value = BigInt(0)) => {
|
|
21425
|
+
return {
|
|
21426
|
+
to: address,
|
|
21427
|
+
data: payload,
|
|
21428
|
+
value
|
|
21429
|
+
};
|
|
21430
|
+
};
|
|
21431
|
+
|
|
21423
21432
|
// ../../node_modules/.pnpm/viem@2.48.4_typescript@5.9.3_zod@4.4.3/node_modules/viem/_esm/index.js
|
|
21424
21433
|
init_exports();
|
|
21425
21434
|
init_encodeAbiParameters2();
|
|
@@ -21594,14 +21603,20 @@ function privateKeyToAccount(privateKey, options = {}) {
|
|
|
21594
21603
|
};
|
|
21595
21604
|
}
|
|
21596
21605
|
|
|
21606
|
+
// src/state/thunks/paymasterWithdrawThunk.ts
|
|
21607
|
+
init_esm();
|
|
21608
|
+
|
|
21597
21609
|
// src/paymaster/fee.ts
|
|
21598
21610
|
var ERC20_TRANSFER_GAS = 100000n;
|
|
21611
|
+
var PER_DIRECT_WITHDRAW_GAS = 400000n;
|
|
21612
|
+
var PER_DIRECT_WITHDRAW_GAS_ERC20 = 500000n;
|
|
21613
|
+
var FORWARD_GAS = 60000n;
|
|
21599
21614
|
var baseGasUnits = {
|
|
21600
|
-
preVerificationGas:
|
|
21615
|
+
preVerificationGas: 85000n,
|
|
21601
21616
|
verificationGasLimit: 50000n,
|
|
21602
21617
|
callGasLimit: 300000n,
|
|
21603
21618
|
paymasterVerificationGasLimit: 350000n,
|
|
21604
|
-
paymasterPostOpGasLimit:
|
|
21619
|
+
paymasterPostOpGasLimit: 50000n
|
|
21605
21620
|
};
|
|
21606
21621
|
function reasonableGasUnits(isERC20) {
|
|
21607
21622
|
if (!isERC20) return baseGasUnits;
|
|
@@ -21610,6 +21625,15 @@ function reasonableGasUnits(isERC20) {
|
|
|
21610
21625
|
paymasterVerificationGasLimit: baseGasUnits.paymasterVerificationGasLimit + ERC20_TRANSFER_GAS
|
|
21611
21626
|
};
|
|
21612
21627
|
}
|
|
21628
|
+
function reasonableGasUnitsForBatch(isERC20, extraWithdrawals, hasUserTailCalls, tailCallsGasEstimate) {
|
|
21629
|
+
const base = reasonableGasUnits(isERC20);
|
|
21630
|
+
const perWithdraw = isERC20 ? PER_DIRECT_WITHDRAW_GAS_ERC20 : PER_DIRECT_WITHDRAW_GAS;
|
|
21631
|
+
const executionTail = hasUserTailCalls ? tailCallsGasEstimate ?? base.callGasLimit : FORWARD_GAS;
|
|
21632
|
+
return {
|
|
21633
|
+
...base,
|
|
21634
|
+
callGasLimit: BigInt(extraWithdrawals) * perWithdraw + executionTail
|
|
21635
|
+
};
|
|
21636
|
+
}
|
|
21613
21637
|
function computeMinimumViableFee(reasonableGasUnits2, maxFeePerGas) {
|
|
21614
21638
|
const baseFeeMultiplier = 1.2;
|
|
21615
21639
|
const decimals = baseFeeMultiplier.toString().split(".")[1]?.length ?? 0;
|
|
@@ -21617,10 +21641,6 @@ function computeMinimumViableFee(reasonableGasUnits2, maxFeePerGas) {
|
|
|
21617
21641
|
const multiply = (base) => base * BigInt(Math.ceil(baseFeeMultiplier * denominator)) / BigInt(denominator);
|
|
21618
21642
|
const requiredGas = reasonableGasUnits2.verificationGasLimit + reasonableGasUnits2.callGasLimit + reasonableGasUnits2.paymasterVerificationGasLimit + reasonableGasUnits2.preVerificationGas + reasonableGasUnits2.paymasterPostOpGasLimit;
|
|
21619
21643
|
const requiredPrefund = requiredGas * maxFeePerGas;
|
|
21620
|
-
console.log("totalGasUnits", requiredGas);
|
|
21621
|
-
console.log("maxFeePerGas", maxFeePerGas);
|
|
21622
|
-
console.log("requiredPrefund", requiredPrefund);
|
|
21623
|
-
console.log("multiply", 1.2);
|
|
21624
21644
|
return multiply(requiredPrefund);
|
|
21625
21645
|
}
|
|
21626
21646
|
|
|
@@ -23317,6 +23337,19 @@ async function getUserOperationGasPrice(client) {
|
|
|
23317
23337
|
fast: parse(result.fast)
|
|
23318
23338
|
};
|
|
23319
23339
|
}
|
|
23340
|
+
async function estimateUserOperationGas2(client, op, entryPoint) {
|
|
23341
|
+
const result = await client.request({
|
|
23342
|
+
method: "eth_estimateUserOperationGas",
|
|
23343
|
+
params: [op, entryPoint]
|
|
23344
|
+
});
|
|
23345
|
+
return {
|
|
23346
|
+
callGasLimit: BigInt(result.callGasLimit),
|
|
23347
|
+
verificationGasLimit: BigInt(result.verificationGasLimit),
|
|
23348
|
+
preVerificationGas: BigInt(result.preVerificationGas),
|
|
23349
|
+
paymasterVerificationGasLimit: BigInt(result.paymasterVerificationGasLimit ?? 0),
|
|
23350
|
+
paymasterPostOpGasLimit: BigInt(result.paymasterPostOpGasLimit ?? 0)
|
|
23351
|
+
};
|
|
23352
|
+
}
|
|
23320
23353
|
async function buildSignedTornadoUserOp({
|
|
23321
23354
|
signer,
|
|
23322
23355
|
chainId,
|
|
@@ -23417,6 +23450,7 @@ var paymasterWithdrawThunk = createAsyncThunk("withdraw/executePaymasterWithdraw
|
|
|
23417
23450
|
},
|
|
23418
23451
|
secretManager,
|
|
23419
23452
|
tailCalls,
|
|
23453
|
+
tailCallsGasEstimate,
|
|
23420
23454
|
...rest
|
|
23421
23455
|
}, { getState, dispatch }) => {
|
|
23422
23456
|
const state = getState();
|
|
@@ -23447,12 +23481,32 @@ var paymasterWithdrawThunk = createAsyncThunk("withdraw/executePaymasterWithdraw
|
|
|
23447
23481
|
);
|
|
23448
23482
|
const bundlerClient = createPaymasterBundlerClient(bundlerUrl);
|
|
23449
23483
|
const { standard: { maxFeePerGas, maxPriorityFeePerGas } } = await getUserOperationGasPrice(bundlerClient);
|
|
23450
|
-
const gasUnits = reasonableGasUnits(poolInfo.isERC20);
|
|
23451
|
-
const ethFee = computeMinimumViableFee(gasUnits, maxFeePerGas);
|
|
23452
|
-
const fee = poolInfo.isERC20 ? await dataService.quoteWeiInToken(BigInt(paymasterAddress), poolInfo.asset, ethFee) : ethFee;
|
|
23453
23484
|
const relayerAddress = BigInt(paymasterAddress);
|
|
23454
23485
|
const bigintChainId = await dataService.getChainId();
|
|
23455
23486
|
const { recipient: originalRecipient, ...restWithoutRecipient } = rest;
|
|
23487
|
+
const quoteFee = async (ethFee) => poolInfo.isERC20 ? dataService.quoteWeiInToken(BigInt(paymasterAddress), poolInfo.asset, ethFee) : ethFee;
|
|
23488
|
+
const prove = async (deposit, recipient, relayer, depositFee) => unwrapResult(await dispatch(withdrawalsProofThunk({
|
|
23489
|
+
...restWithoutRecipient,
|
|
23490
|
+
recipient,
|
|
23491
|
+
deposit,
|
|
23492
|
+
relayerAddress: relayer,
|
|
23493
|
+
fee: depositFee
|
|
23494
|
+
})));
|
|
23495
|
+
const buildPaymasterData = (poolAddress, proof) => {
|
|
23496
|
+
const [root, nullifierHash, proofRecipient, relayerArg, feeArg, refundArg] = proof.args;
|
|
23497
|
+
return encodePaymasterData(
|
|
23498
|
+
poolAcountsMap.get(poolAddress),
|
|
23499
|
+
encodeTornadoAdapterData(
|
|
23500
|
+
proof.proof,
|
|
23501
|
+
root,
|
|
23502
|
+
nullifierHash,
|
|
23503
|
+
proofRecipient,
|
|
23504
|
+
relayerArg,
|
|
23505
|
+
BigInt(feeArg),
|
|
23506
|
+
BigInt(refundArg)
|
|
23507
|
+
)
|
|
23508
|
+
);
|
|
23509
|
+
};
|
|
23456
23510
|
const ephemeralSigner = async (deposit) => privateKeyToAccount(await secretManager.deriveEphemeralSigner({
|
|
23457
23511
|
depositIndex: deposit.index,
|
|
23458
23512
|
chainId: bigintChainId,
|
|
@@ -23467,78 +23521,120 @@ var paymasterWithdrawThunk = createAsyncThunk("withdraw/executePaymasterWithdraw
|
|
|
23467
23521
|
return ephemeralSigner(deposits[0]);
|
|
23468
23522
|
};
|
|
23469
23523
|
const resolveIndependentSigner = (deposit) => delegation?.mode === "deterministic" ? ephemeralSigner(deposit) : Promise.resolve(privateKeyToAccount(generatePrivateKey()));
|
|
23470
|
-
const
|
|
23471
|
-
|
|
23472
|
-
|
|
23473
|
-
|
|
23474
|
-
const
|
|
23475
|
-
const
|
|
23476
|
-
const
|
|
23477
|
-
const
|
|
23478
|
-
|
|
23479
|
-
|
|
23480
|
-
|
|
23481
|
-
|
|
23482
|
-
|
|
23483
|
-
|
|
23484
|
-
|
|
23485
|
-
|
|
23486
|
-
|
|
23487
|
-
|
|
23488
|
-
|
|
23489
|
-
|
|
23490
|
-
|
|
23491
|
-
|
|
23492
|
-
|
|
23493
|
-
|
|
23494
|
-
|
|
23495
|
-
|
|
23496
|
-
|
|
23497
|
-
|
|
23498
|
-
|
|
23499
|
-
|
|
23500
|
-
|
|
23501
|
-
|
|
23502
|
-
|
|
23503
|
-
|
|
23504
|
-
|
|
23505
|
-
|
|
23506
|
-
|
|
23507
|
-
|
|
23508
|
-
|
|
23509
|
-
|
|
23510
|
-
|
|
23511
|
-
|
|
23512
|
-
|
|
23513
|
-
maxPriorityFeePerGas,
|
|
23514
|
-
tailCalls: effectiveTailCalls,
|
|
23515
|
-
// When sharing one delegator across multiple deposits each userOp needs
|
|
23516
|
-
// a distinct nonce (0, 1, 2 …) on the shared sender.
|
|
23517
|
-
nonce: sharedDelegator ? BigInt(i) : 0n
|
|
23518
|
-
})
|
|
23519
|
-
);
|
|
23524
|
+
const needsConsolidation = deposits.length > 1 || tailCalls != null;
|
|
23525
|
+
if (!needsConsolidation) {
|
|
23526
|
+
const deposit = deposits[0];
|
|
23527
|
+
const signer2 = await resolveIndependentSigner(deposit);
|
|
23528
|
+
const gasUnits = reasonableGasUnits(poolInfo.isERC20);
|
|
23529
|
+
const fee2 = await quoteFee(computeMinimumViableFee(gasUnits, maxFeePerGas));
|
|
23530
|
+
const proof = await prove(deposit, originalRecipient, relayerAddress, fee2);
|
|
23531
|
+
const userOperation2 = await buildSignedTornadoUserOp({
|
|
23532
|
+
signer: signer2,
|
|
23533
|
+
chainId,
|
|
23534
|
+
paymasterAddress,
|
|
23535
|
+
paymasterData: buildPaymasterData(deposit.pool, proof),
|
|
23536
|
+
gas: { ...gasUnits, callGasLimit: 0n },
|
|
23537
|
+
maxFeePerGas,
|
|
23538
|
+
maxPriorityFeePerGas,
|
|
23539
|
+
nonce: 0n
|
|
23540
|
+
});
|
|
23541
|
+
return [{
|
|
23542
|
+
mode: "paymaster",
|
|
23543
|
+
proof,
|
|
23544
|
+
poolAddress: deposit.pool,
|
|
23545
|
+
isERC20: poolInfo.isERC20,
|
|
23546
|
+
paymasterAddress,
|
|
23547
|
+
entryPointAddress,
|
|
23548
|
+
bundlerUrl,
|
|
23549
|
+
userOperation: userOperation2
|
|
23550
|
+
}];
|
|
23551
|
+
}
|
|
23552
|
+
const signer = await resolveBatchDelegator();
|
|
23553
|
+
const senderAddress = BigInt(signer.address);
|
|
23554
|
+
const sponsoringDeposit = deposits[0];
|
|
23555
|
+
const directDeposits = deposits.slice(1);
|
|
23556
|
+
const ZERO_ADDRESS = 0n;
|
|
23557
|
+
const directWithdrawCalls = [];
|
|
23558
|
+
for (const deposit of directDeposits) {
|
|
23559
|
+
const proof = await prove(deposit, senderAddress, ZERO_ADDRESS, 0n);
|
|
23560
|
+
const [root, nullifierHash, proofRecipient, relayerArg, feeArg, refundArg] = proof.args;
|
|
23561
|
+
const data = encodeFunctionData({
|
|
23562
|
+
abi: poolAbi,
|
|
23563
|
+
functionName: "withdraw",
|
|
23564
|
+
args: [proof.proof, root, nullifierHash, proofRecipient, relayerArg, BigInt(feeArg), BigInt(refundArg)]
|
|
23565
|
+
});
|
|
23566
|
+
directWithdrawCalls.push(createTx(addressToHex(deposit.pool), data, 0n));
|
|
23520
23567
|
}
|
|
23521
|
-
|
|
23568
|
+
const totalDenomination = deposits.reduce(
|
|
23569
|
+
(sum, d) => sum + pools.get(d.pool).denomination,
|
|
23570
|
+
0n
|
|
23571
|
+
);
|
|
23572
|
+
const forwardCalls = (fee2) => {
|
|
23573
|
+
const amount2 = totalDenomination - fee2;
|
|
23574
|
+
if (poolInfo.isERC20) {
|
|
23575
|
+
const data = encodeFunctionData({
|
|
23576
|
+
abi: erc20Abi,
|
|
23577
|
+
functionName: "transfer",
|
|
23578
|
+
args: [addressToHex(originalRecipient), amount2]
|
|
23579
|
+
});
|
|
23580
|
+
return [createTx(addressToHex(poolInfo.asset), data, 0n)];
|
|
23581
|
+
}
|
|
23582
|
+
return [createTx(addressToHex(originalRecipient), "0x", amount2)];
|
|
23583
|
+
};
|
|
23584
|
+
const composeTailCalls = (fee2) => async (addr) => [
|
|
23585
|
+
...directWithdrawCalls,
|
|
23586
|
+
...tailCalls ? await tailCalls(addr) : forwardCalls(fee2)
|
|
23587
|
+
];
|
|
23588
|
+
const buildOp = (gas2, fee2, paymasterData) => buildSignedTornadoUserOp({
|
|
23589
|
+
signer,
|
|
23590
|
+
chainId,
|
|
23591
|
+
paymasterAddress,
|
|
23592
|
+
paymasterData,
|
|
23593
|
+
gas: gas2,
|
|
23594
|
+
maxFeePerGas,
|
|
23595
|
+
maxPriorityFeePerGas,
|
|
23596
|
+
tailCalls: composeTailCalls(fee2),
|
|
23597
|
+
nonce: 0n
|
|
23598
|
+
});
|
|
23599
|
+
const baselineGas = reasonableGasUnitsForBatch(
|
|
23600
|
+
poolInfo.isERC20,
|
|
23601
|
+
directDeposits.length,
|
|
23602
|
+
tailCalls != null,
|
|
23603
|
+
tailCallsGasEstimate
|
|
23604
|
+
);
|
|
23605
|
+
let fee = await quoteFee(computeMinimumViableFee(baselineGas, maxFeePerGas));
|
|
23606
|
+
let gas = baselineGas;
|
|
23607
|
+
let sponsoringProof = await prove(sponsoringDeposit, senderAddress, relayerAddress, fee);
|
|
23608
|
+
let userOperation = await buildOp(gas, fee, buildPaymasterData(sponsoringDeposit.pool, sponsoringProof));
|
|
23609
|
+
try {
|
|
23610
|
+
const estimated = await estimateUserOperationGas2(bundlerClient, userOperation, entryPointAddress);
|
|
23611
|
+
const buffer2 = (x) => x * 12n / 10n;
|
|
23612
|
+
const orBaseline = (e, b) => e > 0n ? buffer2(e) : b;
|
|
23613
|
+
const refinedGas = {
|
|
23614
|
+
callGasLimit: orBaseline(estimated.callGasLimit, baselineGas.callGasLimit),
|
|
23615
|
+
verificationGasLimit: orBaseline(estimated.verificationGasLimit, baselineGas.verificationGasLimit),
|
|
23616
|
+
preVerificationGas: orBaseline(estimated.preVerificationGas, baselineGas.preVerificationGas),
|
|
23617
|
+
paymasterVerificationGasLimit: orBaseline(estimated.paymasterVerificationGasLimit, baselineGas.paymasterVerificationGasLimit),
|
|
23618
|
+
paymasterPostOpGasLimit: orBaseline(estimated.paymasterPostOpGasLimit, baselineGas.paymasterPostOpGasLimit)
|
|
23619
|
+
};
|
|
23620
|
+
fee = await quoteFee(computeMinimumViableFee(refinedGas, maxFeePerGas));
|
|
23621
|
+
gas = refinedGas;
|
|
23622
|
+
sponsoringProof = await prove(sponsoringDeposit, senderAddress, relayerAddress, fee);
|
|
23623
|
+
userOperation = await buildOp(gas, fee, buildPaymasterData(sponsoringDeposit.pool, sponsoringProof));
|
|
23624
|
+
} catch {
|
|
23625
|
+
}
|
|
23626
|
+
return [{
|
|
23522
23627
|
mode: "paymaster",
|
|
23523
|
-
proof,
|
|
23524
|
-
poolAddress,
|
|
23628
|
+
proof: sponsoringProof,
|
|
23629
|
+
poolAddress: sponsoringDeposit.pool,
|
|
23525
23630
|
isERC20: poolInfo.isERC20,
|
|
23526
23631
|
paymasterAddress,
|
|
23527
23632
|
entryPointAddress,
|
|
23528
23633
|
bundlerUrl,
|
|
23529
|
-
userOperation
|
|
23530
|
-
}
|
|
23634
|
+
userOperation
|
|
23635
|
+
}];
|
|
23531
23636
|
});
|
|
23532
23637
|
|
|
23533
|
-
// ../provider/dist/index.js
|
|
23534
|
-
var createTx = (address, payload, value = BigInt(0)) => {
|
|
23535
|
-
return {
|
|
23536
|
-
to: address,
|
|
23537
|
-
data: payload,
|
|
23538
|
-
value
|
|
23539
|
-
};
|
|
23540
|
-
};
|
|
23541
|
-
|
|
23542
23638
|
// src/account/tx/shield.ts
|
|
23543
23639
|
init_esm();
|
|
23544
23640
|
init_esm();
|
|
@@ -23821,7 +23917,8 @@ var storeStateManager = async ({
|
|
|
23821
23917
|
delegation: params.delegation
|
|
23822
23918
|
},
|
|
23823
23919
|
secretManager,
|
|
23824
|
-
tailCalls: params.tailCalls
|
|
23920
|
+
tailCalls: params.tailCalls,
|
|
23921
|
+
tailCallsGasEstimate: params.tailCallsGasEstimate
|
|
23825
23922
|
})
|
|
23826
23923
|
)
|
|
23827
23924
|
);
|
|
@@ -211,6 +211,12 @@ interface IPaymasterWithdrawParams extends IWithdrawBaseParams {
|
|
|
211
211
|
mode: 'paymaster';
|
|
212
212
|
delegation?: DelegationConfig;
|
|
213
213
|
tailCalls?: (address: AccountId) => Promise<TxData[]>;
|
|
214
|
+
/**
|
|
215
|
+
* Optional baseline for the execution-phase gas of user `tailCalls` (not
|
|
216
|
+
* including per-note direct withdraws). Used when bundler estimation fails;
|
|
217
|
+
* when estimation succeeds the bundler estimate still wins.
|
|
218
|
+
*/
|
|
219
|
+
tailCallsGasEstimate?: bigint;
|
|
214
220
|
}
|
|
215
221
|
type IWithdrawapOperationParams = IRelayerWithdrawParams | IPaymasterWithdrawParams;
|
|
216
222
|
interface IGetNotesParams extends IBaseOperationParams {
|
|
@@ -212,6 +212,12 @@ interface IPaymasterWithdrawParams extends IWithdrawBaseParams {
|
|
|
212
212
|
mode: 'paymaster';
|
|
213
213
|
delegation?: DelegationConfig;
|
|
214
214
|
tailCalls?: (address: AccountId) => Promise<TxData[]>;
|
|
215
|
+
/**
|
|
216
|
+
* Optional baseline for the execution-phase gas of user `tailCalls` (not
|
|
217
|
+
* including per-note direct withdraws). Used when bundler estimation fails;
|
|
218
|
+
* when estimation succeeds the bundler estimate still wins.
|
|
219
|
+
*/
|
|
220
|
+
tailCallsGasEstimate?: bigint;
|
|
215
221
|
}
|
|
216
222
|
type IWithdrawapOperationParams = IRelayerWithdrawParams | IPaymasterWithdrawParams;
|
|
217
223
|
interface IGetNotesParams extends IBaseOperationParams {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kohaku-eth/tornado-cash",
|
|
3
|
-
"version": "0.0.2-alpha.
|
|
3
|
+
"version": "0.0.2-alpha.18",
|
|
4
4
|
"description": "Tornado Cash Shielded Pool Kohaku Plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"ox": "^0.12.0",
|
|
76
76
|
"viem": "^2.51.3",
|
|
77
77
|
"@kohaku-eth/plugins": "0.0.1-alpha.11",
|
|
78
|
-
"@kohaku-eth/
|
|
79
|
-
"@kohaku-eth/
|
|
78
|
+
"@kohaku-eth/provider": "0.1.0-alpha.8",
|
|
79
|
+
"@kohaku-eth/mimc-tree": "0.0.3"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@types/node": "^24.12.2",
|