@whetstone-research/doppler-sdk 1.0.34 → 1.0.35

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.
@@ -7662,6 +7662,173 @@ function encodeRehypeDopplerHookMigratorCalldata(params) {
7662
7662
  ]
7663
7663
  );
7664
7664
  }
7665
+ var AirlockCreateReceiptError = class extends Error {
7666
+ code;
7667
+ expected;
7668
+ actual;
7669
+ constructor(code, options = {}) {
7670
+ super(`Airlock Create receipt verification failed: ${code}`);
7671
+ this.name = "AirlockCreateReceiptError";
7672
+ this.code = code;
7673
+ this.expected = options.expected;
7674
+ this.actual = options.actual;
7675
+ }
7676
+ };
7677
+ function addressesEqual(left, right) {
7678
+ return left?.toLowerCase() === right.toLowerCase();
7679
+ }
7680
+ function decodeAirlockCreateLogs(receipt, expectedAirlock) {
7681
+ const matches = [];
7682
+ for (const log of receipt.logs) {
7683
+ if (!addressesEqual(log.address, expectedAirlock)) continue;
7684
+ try {
7685
+ const decoded = viem.decodeEventLog({
7686
+ abi: airlockAbi,
7687
+ data: log.data,
7688
+ topics: log.topics,
7689
+ eventName: "Create"
7690
+ });
7691
+ const args = decoded.args;
7692
+ matches.push({
7693
+ airlock: log.address,
7694
+ tokenAddress: args.asset,
7695
+ numeraire: args.numeraire,
7696
+ initializer: args.initializer,
7697
+ poolOrHookAddress: args.poolOrHook,
7698
+ logIndex: log.logIndex
7699
+ });
7700
+ } catch {
7701
+ }
7702
+ }
7703
+ return matches;
7704
+ }
7705
+ function parseAirlockCreateReceipt({
7706
+ receipt,
7707
+ expectedAirlock
7708
+ }) {
7709
+ const matches = decodeAirlockCreateLogs(receipt, expectedAirlock);
7710
+ if (matches.length === 0) return null;
7711
+ if (matches.length > 1) {
7712
+ throw new AirlockCreateReceiptError("MULTIPLE_CREATE_EVENTS", {
7713
+ expected: 1,
7714
+ actual: matches.length
7715
+ });
7716
+ }
7717
+ return {
7718
+ ...matches[0],
7719
+ transactionHash: receipt.transactionHash,
7720
+ blockNumber: receipt.blockNumber
7721
+ };
7722
+ }
7723
+ function assertAddressMatch(code, expected, actual) {
7724
+ if (!addressesEqual(actual, expected)) {
7725
+ throw new AirlockCreateReceiptError(code, { expected, actual });
7726
+ }
7727
+ }
7728
+ function verifyPreparedCreateReceipt({
7729
+ prepared,
7730
+ receipt
7731
+ }) {
7732
+ if (receipt.status !== "success") {
7733
+ throw new AirlockCreateReceiptError("RECEIPT_FAILED", {
7734
+ expected: "success",
7735
+ actual: receipt.status
7736
+ });
7737
+ }
7738
+ assertAddressMatch(
7739
+ "WRONG_TRANSACTION_TARGET",
7740
+ prepared.transaction.to,
7741
+ receipt.to
7742
+ );
7743
+ assertAddressMatch(
7744
+ "WRONG_TRANSACTION_SENDER",
7745
+ prepared.account,
7746
+ receipt.from
7747
+ );
7748
+ const receiptIdentity = parseAirlockCreateReceipt({
7749
+ receipt,
7750
+ expectedAirlock: prepared.airlock
7751
+ });
7752
+ if (!receiptIdentity) {
7753
+ throw new AirlockCreateReceiptError("MISSING_CREATE_EVENT", {
7754
+ expected: prepared.airlock,
7755
+ actual: null
7756
+ });
7757
+ }
7758
+ assertAddressMatch(
7759
+ "TOKEN_MISMATCH",
7760
+ prepared.prediction.tokenAddress,
7761
+ receiptIdentity.tokenAddress
7762
+ );
7763
+ assertAddressMatch(
7764
+ "NUMERAIRE_MISMATCH",
7765
+ prepared.createParams.numeraire,
7766
+ receiptIdentity.numeraire
7767
+ );
7768
+ assertAddressMatch(
7769
+ "INITIALIZER_MISMATCH",
7770
+ prepared.createParams.poolInitializer,
7771
+ receiptIdentity.initializer
7772
+ );
7773
+ assertAddressMatch(
7774
+ "POOL_OR_HOOK_MISMATCH",
7775
+ prepared.prediction.poolOrHookAddress,
7776
+ receiptIdentity.poolOrHookAddress
7777
+ );
7778
+ return {
7779
+ receiptIdentity,
7780
+ preparedIdentity: {
7781
+ chainId: prepared.chainId,
7782
+ tokenAddress: prepared.prediction.tokenAddress,
7783
+ poolOrHookAddress: prepared.prediction.poolOrHookAddress,
7784
+ governanceAddress: prepared.prediction.governanceAddress,
7785
+ timelockAddress: prepared.prediction.timelockAddress,
7786
+ migrationPoolAddress: prepared.prediction.migrationPoolAddress,
7787
+ poolKey: prepared.prediction.poolKey,
7788
+ poolId: prepared.prediction.poolId,
7789
+ tokenIsCurrency0: prepared.prediction.tokenIsCurrency0
7790
+ }
7791
+ };
7792
+ }
7793
+ async function verifyPreparedCreateExecution({
7794
+ prepared,
7795
+ receipt,
7796
+ publicClient
7797
+ }) {
7798
+ const verified = verifyPreparedCreateReceipt({ prepared, receipt });
7799
+ const transaction = await publicClient.getTransaction({
7800
+ hash: receipt.transactionHash
7801
+ });
7802
+ if (transaction.hash.toLowerCase() !== receipt.transactionHash.toLowerCase()) {
7803
+ throw new AirlockCreateReceiptError("TRANSACTION_HASH_MISMATCH", {
7804
+ expected: receipt.transactionHash,
7805
+ actual: transaction.hash
7806
+ });
7807
+ }
7808
+ assertAddressMatch(
7809
+ "WRONG_TRANSACTION_TARGET",
7810
+ prepared.transaction.to,
7811
+ transaction.to
7812
+ );
7813
+ assertAddressMatch(
7814
+ "WRONG_TRANSACTION_SENDER",
7815
+ prepared.account,
7816
+ transaction.from
7817
+ );
7818
+ if (transaction.input.toLowerCase() !== prepared.transaction.data.toLowerCase()) {
7819
+ throw new AirlockCreateReceiptError("TRANSACTION_INPUT_MISMATCH", {
7820
+ expected: viem.keccak256(prepared.transaction.data),
7821
+ actual: viem.keccak256(transaction.input)
7822
+ });
7823
+ }
7824
+ if (transaction.value !== prepared.transaction.value) {
7825
+ throw new AirlockCreateReceiptError("TRANSACTION_VALUE_MISMATCH", {
7826
+ expected: prepared.transaction.value.toString(),
7827
+ actual: transaction.value.toString()
7828
+ });
7829
+ }
7830
+ return verified;
7831
+ }
7665
7832
 
7666
7833
  // src/evm/utils/marketCapHelpers.ts
7667
7834
  function getMaxTickRounded(tickSpacing) {
@@ -10617,25 +10784,45 @@ var DopplerFactory = class {
10617
10784
  }
10618
10785
  throw new Error("Unable to mine opening auction salt");
10619
10786
  }
10620
- async resolveCreateGasEstimate(args) {
10787
+ isCreateGasRevert(error) {
10788
+ if (error instanceof viem.ContractFunctionRevertedError || error instanceof viem.ExecutionRevertedError) {
10789
+ return true;
10790
+ }
10791
+ if (!(error instanceof viem.BaseError)) return false;
10792
+ return Boolean(
10793
+ error.walk(
10794
+ (cause) => cause instanceof viem.ContractFunctionRevertedError || cause instanceof viem.ExecutionRevertedError
10795
+ )
10796
+ );
10797
+ }
10798
+ async resolveInternalCreateGasEstimate(args) {
10621
10799
  const { request, address, createParams, account } = args;
10622
- const gasFromRequest = request && typeof request === "object" && "gas" in request ? request.gas : void 0;
10623
- if (gasFromRequest) {
10624
- return gasFromRequest;
10800
+ let gasFromRequest;
10801
+ if (request && typeof request === "object" && "gas" in request && typeof request.gas === "bigint") {
10802
+ gasFromRequest = request.gas;
10803
+ }
10804
+ if (gasFromRequest !== void 0) {
10805
+ return { status: "estimated", gas: gasFromRequest };
10625
10806
  }
10626
10807
  try {
10627
- const estimated = await this.publicClient.estimateContractGas({
10628
- address,
10629
- abi: airlockAbi,
10630
- functionName: "create",
10631
- args: [{ ...createParams }],
10632
- account
10633
- });
10634
- return estimated;
10635
- } catch {
10636
- return void 0;
10808
+ const gas = await this.publicClient.estimateContractGas(
10809
+ {
10810
+ address,
10811
+ abi: airlockAbi,
10812
+ functionName: "create",
10813
+ args: [{ ...createParams }],
10814
+ account
10815
+ }
10816
+ );
10817
+ return { status: "estimated", gas };
10818
+ } catch (error) {
10819
+ return this.isCreateGasRevert(error) ? { status: "reverted", error } : { status: "unavailable" };
10637
10820
  }
10638
10821
  }
10822
+ async resolveCreateGasEstimate(args) {
10823
+ const estimate = await this.resolveInternalCreateGasEstimate(args);
10824
+ return estimate.status === "estimated" ? estimate.gas : void 0;
10825
+ }
10639
10826
  isDoppler404Token(token) {
10640
10827
  return token.type === "doppler404";
10641
10828
  }
@@ -11391,86 +11578,157 @@ var DopplerFactory = class {
11391
11578
  };
11392
11579
  return createParams;
11393
11580
  }
11394
- async simulateCreateMulticurve(params) {
11581
+ async resolveFinalMulticurveCreate(args) {
11582
+ const { params, simulationAccount } = args;
11395
11583
  const addresses = getAddresses(this.chainId);
11396
- const createParams = this.encodeCreateMulticurveParams(params);
11397
- const airlockAddress = params.modules?.airlock ?? addresses.airlock;
11398
- const { request, result } = await this.publicClient.simulateContract({
11399
- address: airlockAddress,
11584
+ const airlock = params.modules?.airlock ?? addresses.airlock;
11585
+ const initialCreateParams = args.createParams ?? this.encodeCreateMulticurveParams(params);
11586
+ const initialSimulation = await this.publicClient.simulateContract({
11587
+ address: airlock,
11400
11588
  abi: airlockAbi,
11401
11589
  functionName: "create",
11402
- args: [{ ...createParams }],
11403
- account: this.walletClient?.account
11404
- });
11405
- const simResult = result;
11406
- const gasEstimate = await this.resolveCreateGasEstimate({
11407
- request,
11408
- address: airlockAddress,
11409
- createParams,
11410
- account: this.walletClient?.account ?? params.userAddress
11590
+ args: [{ ...initialCreateParams }],
11591
+ account: simulationAccount
11411
11592
  });
11412
- if (!simResult || !Array.isArray(simResult) || simResult.length < 2) {
11593
+ const initialResult = initialSimulation.result;
11594
+ if (!initialResult || !Array.isArray(initialResult) || initialResult.length < 5) {
11413
11595
  throw new Error("Failed to simulate multicurve create");
11414
11596
  }
11415
- const tokenAddress = simResult[0];
11416
- const poolId = await this.computeMulticurvePoolId(params, tokenAddress);
11417
- const createParamsWithTimelock = this.withSimulationGovernanceTimelockExclusion({
11418
- createParams,
11419
- simResult,
11420
- token: params.token,
11421
- governance: params.governance,
11422
- modules: params.modules,
11423
- addresses
11597
+ let createParams = initialCreateParams;
11598
+ let request = initialSimulation.request;
11599
+ let result = initialResult;
11600
+ if (!args.createParams) {
11601
+ const enrichedCreateParams = this.withSimulationGovernanceTimelockExclusion({
11602
+ createParams,
11603
+ simResult: initialResult,
11604
+ token: params.token,
11605
+ governance: params.governance,
11606
+ modules: params.modules,
11607
+ addresses
11608
+ });
11609
+ if (enrichedCreateParams.tokenFactoryData !== createParams.tokenFactoryData) {
11610
+ createParams = enrichedCreateParams;
11611
+ const finalSimulation = await this.publicClient.simulateContract({
11612
+ address: airlock,
11613
+ abi: airlockAbi,
11614
+ functionName: "create",
11615
+ args: [{ ...createParams }],
11616
+ account: simulationAccount
11617
+ });
11618
+ const finalResult = finalSimulation.result;
11619
+ if (!finalResult || !Array.isArray(finalResult) || finalResult.length < 5) {
11620
+ throw new Error("Failed to simulate enriched multicurve create");
11621
+ }
11622
+ request = finalSimulation.request;
11623
+ result = finalResult;
11624
+ }
11625
+ }
11626
+ const tokenAddress = result[0];
11627
+ const poolIdentity = await this.computeMulticurvePoolIdentity(
11628
+ params,
11629
+ tokenAddress
11630
+ );
11631
+ const prediction = {
11632
+ tokenAddress,
11633
+ poolOrHookAddress: result[1],
11634
+ governanceAddress: result[2],
11635
+ timelockAddress: result[3],
11636
+ migrationPoolAddress: result[4],
11637
+ ...poolIdentity
11638
+ };
11639
+ return { airlock, createParams, prediction, request };
11640
+ }
11641
+ async prepareCreateMulticurve(params, options) {
11642
+ const resolved = await this.resolveFinalMulticurveCreate({
11643
+ params,
11644
+ simulationAccount: options.account
11645
+ });
11646
+ const internalGasEstimate = await this.resolveInternalCreateGasEstimate({
11647
+ request: resolved.request,
11648
+ address: resolved.airlock,
11649
+ createParams: resolved.createParams,
11650
+ account: options.account
11424
11651
  });
11652
+ if (internalGasEstimate.status === "reverted") {
11653
+ throw internalGasEstimate.error;
11654
+ }
11655
+ const gasEstimate = internalGasEstimate;
11425
11656
  return {
11426
- createParams: createParamsWithTimelock,
11427
- tokenAddress,
11428
- poolId,
11657
+ chainId: this.chainId,
11658
+ account: options.account,
11659
+ airlock: resolved.airlock,
11660
+ createParams: resolved.createParams,
11661
+ prediction: resolved.prediction,
11662
+ transaction: {
11663
+ to: resolved.airlock,
11664
+ data: viem.encodeFunctionData({
11665
+ abi: airlockAbi,
11666
+ functionName: "create",
11667
+ args: [{ ...resolved.createParams }]
11668
+ }),
11669
+ value: 0n
11670
+ },
11671
+ gasEstimate
11672
+ };
11673
+ }
11674
+ async simulateCreateMulticurve(params) {
11675
+ const resolved = await this.resolveFinalMulticurveCreate({
11676
+ params,
11677
+ simulationAccount: this.walletClient?.account
11678
+ });
11679
+ const gasEstimate = await this.resolveCreateGasEstimate({
11680
+ request: resolved.request,
11681
+ address: resolved.airlock,
11682
+ createParams: resolved.createParams,
11683
+ account: this.walletClient?.account ?? params.userAddress
11684
+ });
11685
+ return {
11686
+ createParams: resolved.createParams,
11687
+ tokenAddress: resolved.prediction.tokenAddress,
11688
+ poolId: resolved.prediction.poolId,
11429
11689
  gasEstimate,
11430
11690
  execute: () => this.createMulticurve(params, {
11431
- _createParams: createParamsWithTimelock
11691
+ _createParams: resolved.createParams
11432
11692
  })
11433
11693
  };
11434
11694
  }
11435
11695
  async createMulticurve(params, options) {
11436
- const addresses = getAddresses(this.chainId);
11437
- if (!this.walletClient)
11696
+ if (!this.walletClient) {
11438
11697
  throw new Error("Wallet client required for write operations");
11439
- const createParams = options?._createParams ?? (await this.simulateCreateMulticurve(params)).createParams;
11440
- const airlockAddress = params.modules?.airlock ?? addresses.airlock;
11441
- const { request, result } = await this.publicClient.simulateContract({
11442
- address: airlockAddress,
11443
- abi: airlockAbi,
11444
- functionName: "create",
11445
- args: [{ ...createParams }],
11446
- account: this.walletClient.account
11698
+ }
11699
+ const resolved = await this.resolveFinalMulticurveCreate({
11700
+ params,
11701
+ simulationAccount: this.walletClient.account,
11702
+ createParams: options?._createParams
11447
11703
  });
11448
- const simResult = result;
11449
11704
  const gasEstimate = await this.resolveCreateGasEstimate({
11450
- request,
11451
- address: airlockAddress,
11452
- createParams,
11705
+ request: resolved.request,
11706
+ address: resolved.airlock,
11707
+ createParams: resolved.createParams,
11453
11708
  account: this.walletClient.account
11454
11709
  });
11455
11710
  const gas = params.gas ?? gasEstimate ?? DEFAULT_CREATE_GAS_LIMIT;
11456
- const hash = await this.walletClient.writeContract({ ...request, gas });
11711
+ const hash = await this.walletClient.writeContract({
11712
+ ...resolved.request,
11713
+ gas
11714
+ });
11457
11715
  const receipt = await this.publicClient.waitForTransactionReceipt({ hash, confirmations: 2 });
11458
- const actualAddresses = this.extractAddressesFromCreateEvent(receipt);
11459
- if (!actualAddresses) {
11716
+ const createResult = parseAirlockCreateReceipt({
11717
+ receipt,
11718
+ expectedAirlock: resolved.airlock
11719
+ });
11720
+ if (!createResult) {
11460
11721
  throw new Error(
11461
11722
  "Failed to extract token address from Create event in transaction logs"
11462
11723
  );
11463
11724
  }
11464
- const actualTokenAddress = actualAddresses.tokenAddress;
11465
- if (simResult && Array.isArray(simResult) && simResult.length >= 1) {
11466
- const simulatedToken = simResult[0];
11467
- if (simulatedToken.toLowerCase() !== actualTokenAddress.toLowerCase()) {
11468
- console.warn(
11469
- `[DopplerSDK] Simulation predicted token ${simulatedToken} but actual is ${actualTokenAddress}. This may indicate state divergence between simulation and execution.`
11470
- );
11471
- }
11725
+ const actualTokenAddress = createResult.tokenAddress;
11726
+ if (resolved.prediction.tokenAddress.toLowerCase() !== actualTokenAddress.toLowerCase()) {
11727
+ console.warn(
11728
+ `[DopplerSDK] Simulation predicted token ${resolved.prediction.tokenAddress} but actual is ${actualTokenAddress}. This may indicate state divergence between simulation and execution.`
11729
+ );
11472
11730
  }
11473
- const poolId = await this.computeMulticurvePoolId(
11731
+ const { poolId } = await this.computeMulticurvePoolIdentity(
11474
11732
  params,
11475
11733
  actualTokenAddress
11476
11734
  );
@@ -12653,10 +12911,10 @@ var DopplerFactory = class {
12653
12911
  return viem.keccak256(encoded);
12654
12912
  }
12655
12913
  /**
12656
- * Compute the V4 poolId for a multicurve pool from the same pool-key fields
12657
- * the initializer will register on-chain.
12914
+ * Compute the complete V4 multicurve pool identity from the same pool-key
12915
+ * fields the initializer will register on-chain.
12658
12916
  */
12659
- async computeMulticurvePoolId(params, tokenAddress) {
12917
+ async computeMulticurvePoolIdentity(params, tokenAddress) {
12660
12918
  const addresses = getAddresses(this.chainId);
12661
12919
  const initializerMode = this.resolveMulticurveInitializerMode(params);
12662
12920
  let hookAddress;
@@ -12686,16 +12944,18 @@ var DopplerFactory = class {
12686
12944
  }
12687
12945
  const numeraire = params.sale.numeraire;
12688
12946
  const tokenIsCurrency0 = BigInt(tokenAddress) < BigInt(numeraire);
12689
- const currency0 = tokenIsCurrency0 ? tokenAddress : numeraire;
12690
- const currency1 = tokenIsCurrency0 ? numeraire : tokenAddress;
12691
- const fee = initializerMode.type === "decay" || initializerMode.type === "rehype" && initializerMode.hookConfig ? DYNAMIC_FEE_FLAG : params.pool.fee;
12692
- return this.computePoolId({
12693
- currency0,
12694
- currency1,
12695
- fee,
12947
+ const poolKey = {
12948
+ currency0: tokenIsCurrency0 ? tokenAddress : numeraire,
12949
+ currency1: tokenIsCurrency0 ? numeraire : tokenAddress,
12950
+ fee: initializerMode.type === "decay" || initializerMode.type === "rehype" && initializerMode.hookConfig ? DYNAMIC_FEE_FLAG : params.pool.fee,
12696
12951
  tickSpacing: params.pool.tickSpacing,
12697
12952
  hooks: hookAddress
12698
- });
12953
+ };
12954
+ return {
12955
+ poolKey,
12956
+ poolId: this.computePoolId(poolKey),
12957
+ tokenIsCurrency0
12958
+ };
12699
12959
  }
12700
12960
  async ensureMulticurveBundlerSupport(bundler) {
12701
12961
  if (this.multicurveBundlerSupport.get(bundler)) {
@@ -20342,6 +20602,7 @@ var DopplerSDK = class {
20342
20602
  var VERSION = "1.0.0";
20343
20603
 
20344
20604
  exports.ADDRESSES = ADDRESSES;
20605
+ exports.AirlockCreateReceiptError = AirlockCreateReceiptError;
20345
20606
  exports.BASIS_POINTS = BASIS_POINTS;
20346
20607
  exports.CHAIN_IDS = CHAIN_IDS;
20347
20608
  exports.DAY_SECONDS = DAY_SECONDS;
@@ -20511,6 +20772,7 @@ exports.normalizeRehypeDopplerHookInitializerConfig = normalizeRehypeDopplerHook
20511
20772
  exports.openingAuctionAbi = openingAuctionAbi;
20512
20773
  exports.openingAuctionInitializerAbi = openingAuctionInitializerAbi;
20513
20774
  exports.openingAuctionPositionManagerAbi = openingAuctionPositionManagerAbi;
20775
+ exports.parseAirlockCreateReceipt = parseAirlockCreateReceipt;
20514
20776
  exports.poolManagerAbi = poolManagerAbi;
20515
20777
  exports.priceToSqrtPriceX96 = priceToSqrtPriceX96;
20516
20778
  exports.priceToTick = priceToTick;
@@ -20539,6 +20801,8 @@ exports.v4MulticurveInitializerAbi = v4MulticurveInitializerAbi;
20539
20801
  exports.v4MulticurveMigratorAbi = v4MulticurveMigratorAbi;
20540
20802
  exports.v4QuoterAbi = v4QuoterAbi;
20541
20803
  exports.validateMarketCapParameters = validateMarketCapParameters;
20804
+ exports.verifyPreparedCreateExecution = verifyPreparedCreateExecution;
20805
+ exports.verifyPreparedCreateReceipt = verifyPreparedCreateReceipt;
20542
20806
  exports.weth9Abi = weth9Abi;
20543
20807
  //# sourceMappingURL=index.cjs.map
20544
20808
  //# sourceMappingURL=index.cjs.map