@whetstone-research/doppler-sdk 1.0.40 → 1.0.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,7 +15,7 @@ The Doppler SDK exposes network-specific entrypoints for creating, managing, and
15
15
  - **Solana Clients and React**: Read clients, PDA helpers, generated codecs, and optional React bindings
16
16
  - **Token Management**: Built-in EVM support for DERC20 tokens with vesting
17
17
  - **Type Safety**: Full TypeScript support across EVM and Solana entrypoints
18
- - **Network Support**: EVM deployments on Base, Arbitrum One, Unichain, Ink, and other supported chains; Solana/SVM support via explicit Solana program deployments
18
+ - **Network Support**: EVM deployments on Ethereum, BNB Smart Chain (BSC), Monad, Robinhood Chain, Arc, Base, and Arbitrum; Solana/SVM support via explicit Solana program deployments
19
19
 
20
20
  ## Installation
21
21
 
@@ -324,76 +324,6 @@ console.log('Hook address:', result.hookAddress);
324
324
  console.log('Token address:', result.tokenAddress);
325
325
  ```
326
326
 
327
- ### Opening Auction (Lifecycle + Bid Management)
328
-
329
- Support includes:
330
-
331
- - `sdk.buildOpeningAuction()` for `CreateOpeningAuctionParams`
332
- - `sdk.factory.simulateCreateOpeningAuction(params)` and `sdk.factory.createOpeningAuction(params)`
333
- - `sdk.getOpeningAuction(hookAddress)` for hook reads + `settleAuction()` / `claimIncentives()`
334
- - `sdk.factory.simulateCompleteOpeningAuction(...)` and `sdk.factory.completeOpeningAuction(...)` for handoff to Doppler
335
- - `sdk.getOpeningAuctionLifecycle(initializerAddress?)` for initializer-level state + complete/recover/sweep helpers
336
- - `sdk.getOpeningAuctionPositionManager(positionManagerAddress?)` for placing/withdrawing opening-auction bids
337
- - Resolve the address via `await (await sdk.getOpeningAuctionLifecycle(initializerAddress)).getPositionManager()` when chain defaults are not configured
338
- - Resolve `positionId` for incentives via `opening.getPositionId(...)` or `opening.claimIncentivesByPositionKey(...)` (no log parsing required)
339
-
340
- > **Base caveat:** on Base mainnet (`chainId = 8453`), `openingAuctionInitializer` and `openingAuctionPositionManager` default to `0x0000000000000000000000000000000000000000` until deployment. Override with `.withOpeningAuctionInitializer('0x...')` / `.withOpeningAuctionPositionManager('0x...')` (or pass explicit addresses) before using opening-auction create/completion/bid flows there.
341
-
342
- ```typescript
343
- const params = sdk
344
- .buildOpeningAuction()
345
- .tokenConfig({
346
- name: 'My Token',
347
- symbol: 'MTK',
348
- tokenURI: 'https://example.com/metadata.json',
349
- })
350
- .saleConfig({
351
- initialSupply: parseEther('1000000'),
352
- numTokensToSell: parseEther('900000'),
353
- numeraire: '0x...',
354
- })
355
- .openingAuctionConfig({
356
- auctionDuration: 3600,
357
- minAcceptableTickToken0: -887220,
358
- minAcceptableTickToken1: -887220,
359
- incentiveShareBps: 500,
360
- tickSpacing: 60,
361
- fee: 3000,
362
- minLiquidity: 1n,
363
- shareToAuctionBps: 8000,
364
- })
365
- .dopplerConfig({
366
- minProceeds: parseEther('10'),
367
- maxProceeds: parseEther('100'),
368
- startTick: -69080,
369
- endTick: -92103,
370
- })
371
- .withMigration({ type: 'uniswapV4', fee: 3000, tickSpacing: 60 })
372
- .withUserAddress('0x...')
373
- .withOpeningAuctionInitializer('0x...') // required on Base until deployed
374
- .build();
375
-
376
- const sim = await sdk.factory.simulateCreateOpeningAuction(params);
377
- const created = await sim.execute();
378
-
379
- const opening = await sdk.getOpeningAuction(created.openingAuctionHookAddress);
380
- await opening.getPhase();
381
-
382
- const lifecycle = await sdk.getOpeningAuctionLifecycle('0x...');
383
- await lifecycle.getState(created.tokenAddress);
384
-
385
- await sdk.factory.completeOpeningAuction({
386
- asset: created.tokenAddress,
387
- initializerAddress: '0x...',
388
- });
389
- ```
390
-
391
- `completeOpeningAuction` auto-settles and auto-mines `dopplerSalt` when omitted; because completion mining can race with block timestamps/state changes, the SDK may re-mine and retry a few times if needed. `simulateCompleteOpeningAuction` requires the opening auction to already be settled.
392
-
393
- Position-manager bid wrappers are available, but bid sizing is still “advanced user”: `liquidity` is Uniswap V4 liquidity units. Use `simulatePlaceBid(...)` / `simulateWithdrawBid(...)` to inspect the `BalanceDelta` (token amounts in/out) and iterate. During the active auction, liquidity withdrawals must be full (no partial removals); use `withdrawFullBid(...)` to read the onchain liquidity and withdraw safely.
394
-
395
- See [examples/opening-auction-lifecycle.ts](./examples/opening-auction-lifecycle.ts) for the full builder/factory/lifecycle flow, and [examples/opening-auction-bidding.ts](./examples/opening-auction-bidding.ts) for the bid-management pattern + positionId resolution.
396
-
397
327
  ### Multicurve Auction (V4 Multicurve Initializer)
398
328
 
399
329
  Multicurve auctions use `DopplerHookInitializer` by default to seed liquidity across multiple curves in a single Uniswap V4 pool. The typed initializer modes are `dopplerHookInitializer`, `standard`, `scheduled`, `decay`, and `rehype`; use `withV4MulticurveInitializer(address)` when explicitly targeting the legacy standard initializer.
@@ -459,8 +389,7 @@ const salt =
459
389
  '0x1111111111111111111111111111111111111111111111111111111111111111' satisfies Hex;
460
390
 
461
391
  const deterministicParams = { ...params, salt };
462
- const preview =
463
- await sdk.factory.simulateCreateMulticurve(deterministicParams);
392
+ const preview = await sdk.factory.simulateCreateMulticurve(deterministicParams);
464
393
  ```
465
394
 
466
395
  Persist and reuse the salt with otherwise identical inputs for a later
@@ -469,7 +398,6 @@ independent create operation. Builder users can call `.withSalt(salt)` before
469
398
  preserves the generated-salt behavior. Explicit salts must be `0x` followed by
470
399
  exactly 64 hexadecimal characters.
471
400
 
472
-
473
401
  **Market Cap Presets (Low / Medium / High):**
474
402
 
475
403
  ```typescript
@@ -984,7 +912,7 @@ For a runnable release-focused example covering legacy DERC20, DERC20 V2 schedul
984
912
 
985
913
  DopplerERC20V1 is the default token template when `type` is omitted. Set `type: 'dopplerERC20V1'` to make that choice explicit, or set `type: 'standard'` to use the legacy token path, where cliff/allocation vesting routes to the legacy DERC20 template. The SDK uses the configured `dopplerERC20V1Factory` by default; `withTokenFactory(address)` takes precedence but must point to a factory compatible with the selected token path and token data ABI. `controller` is optional and defaults to the zero address, so set it only if early balance-limit disable should be possible.
986
914
 
987
- When balance limiting is enabled on the default DopplerERC20V1 integration, the SDK encodes user exclusions plus determinable protocol recipients for the selected auction path into deployment-time `excludedFromBalanceLimit`, including initializers, hooks, PoolManager, migrators, known migration pools, no-op governance, launchpad governance multisigs, and standard GovernanceFactory timelocks for `default` or `custom` governance. Custom `withTokenFactory(address)` paths receive only the `excludedFromBalanceLimit` entries supplied in `tokenConfig`, so custom token factory users must provide any required deployment-time exclusions themselves. Custom `withGovernanceFactory(address)` paths skip standard-governance timelock auto-exclusion, so custom governance factory users must provide any required timelock exclusions themselves. Exclusions cannot be added later through the controller or governance.
915
+ When balance limiting is enabled on the default DopplerERC20V1 integration, the SDK encodes user exclusions plus determinable protocol recipients for the selected auction path into deployment-time `excludedFromBalanceLimit`, including initializers, hooks, PoolManager, migrators, known migration pools, no-op governance, and launchpad governance multisigs. It cannot safely predict the nonce-based timelock created by `default` or `custom` governance. For those governance modes, the SDK rejects configurations where `initialSupply - numTokensToSell - vesting allocations` exceeds `maxBalanceLimit`, because Airlock would transfer that excess to the non-excluded timelock and revert. Allocate enough tokens to the sale or vesting, increase the limit, or use no-op or launchpad governance. Custom `withTokenFactory(address)` paths receive only the `excludedFromBalanceLimit` entries supplied in `tokenConfig`, so custom token factory users must provide required deployment-time protocol exclusions themselves. Exclusions cannot be added later through the controller or governance.
988
916
 
989
917
  DopplerERC20V1 supports vesting through `withVesting` while staying on the DopplerERC20V1 factory path: use `duration` with optional `cliffDuration` for a shared schedule, or `allocations` for per-beneficiary schedules.
990
918
 
@@ -1246,7 +1174,7 @@ migration: {
1246
1174
  }
1247
1175
  ```
1248
1176
 
1249
- ### Migrate to Uniswap V2 with Proceeds Split + Top-ups
1177
+ ### Migrate to Uniswap V2 with Proceeds Split
1250
1178
 
1251
1179
  ```typescript
1252
1180
  migration: {
@@ -1259,9 +1187,8 @@ migration: {
1259
1187
  ```
1260
1188
 
1261
1189
  - The split recipient receives the configured share of numeraire proceeds during migration.
1262
- - If the asset/numeraire pair was topped up in `TopUpDistributor` before migration, the split recipient also receives those top-ups automatically.
1263
1190
 
1264
- ### Migrate to Uniswap V4 with Proceeds Split + Top-ups
1191
+ ### Migrate to Uniswap V4 with Proceeds Split
1265
1192
 
1266
1193
  ```typescript
1267
1194
  migration: {
@@ -1284,50 +1211,11 @@ migration: {
1284
1211
 
1285
1212
  - `streamableFees` is required for `uniswapV4Split`.
1286
1213
  - Beneficiaries must sum to `1e18`, and the Airlock owner must be included with at least 5% shares.
1287
- - The split recipient also receives any `TopUpDistributor` funds pulled during migration.
1288
-
1289
- ### TopUpDistributor Top-ups
1290
-
1291
- The SDK exposes `sdk.topUpDistributor` and `sdk.getTopUpDistributor(address?)`
1292
- for building, simulating, and submitting `topUp({ asset, numeraire, amount })`
1293
- transactions where `getAddresses(chainId).topUpDistributor` is configured. The helper methods
1294
- accept the same object shape for `buildTopUpTransaction({ asset, numeraire, amount })` and
1295
- `simulateTopUp({ asset, numeraire, amount })`. ETH top-ups use `numeraire = ZERO_ADDRESS` and
1296
- send `value = amount`; ERC20 top-ups send no native value and require the user to approve the
1297
- `TopUpDistributor` before calling `topUp`.
1298
-
1299
- ```typescript
1300
- import { ZERO_ADDRESS } from '@whetstone-research/doppler-sdk/evm';
1301
- import { parseEther } from 'viem';
1302
-
1303
- const topUps = sdk.topUpDistributor;
1304
-
1305
- const tx = topUps.buildTopUpTransaction({
1306
- asset: tokenAddress,
1307
- numeraire: ZERO_ADDRESS,
1308
- amount: parseEther('1'),
1309
- });
1310
-
1311
- const simulation = await topUps.simulateTopUp({
1312
- asset: tokenAddress,
1313
- numeraire: ZERO_ADDRESS,
1314
- amount: parseEther('1'),
1315
- });
1316
-
1317
- await topUps.topUp({
1318
- asset: tokenAddress,
1319
- numeraire: ZERO_ADDRESS,
1320
- amount: parseEther('1'),
1321
- });
1322
- ```
1323
-
1324
- Split migrators pull any TopUpDistributor balance for the asset/numeraire pair during migration
1325
- and pay it to the configured split recipient.
1326
1214
 
1327
1215
  ### Migrate via DopplerHookMigrator (Dynamic Auctions)
1328
1216
 
1329
- Use this mode when you want rehypothecation / custom hook behavior on the
1330
- migrated V4 pool. This migration type is only supported for dynamic auctions.
1217
+ Use this mode when the migrated V4 pool needs an optional generic Doppler hook.
1218
+ This migration type is only supported for dynamic auctions.
1331
1219
 
1332
1220
  ```typescript
1333
1221
  const params = sdk
@@ -1357,72 +1245,27 @@ const params = sdk
1357
1245
  lockDuration: 30 * 24 * 60 * 60,
1358
1246
  beneficiaries: [
1359
1247
  { beneficiary: '0xYourBeneficiary...', shares: parseEther('0.95') },
1360
- await sdk.getAirlockBeneficiary(), // required protocol owner entry (>=5%)
1248
+ await sdk.getAirlockBeneficiary(),
1361
1249
  ],
1362
- rehype: {
1363
- buybackDestination: '0xYourBuybackDestination...',
1364
- customFee: 3000,
1365
- feeRoutingMode: 'directBuyback',
1366
- feeDistributionInfo: {
1367
- assetFeesToAssetBuybackWad: parseEther('0.25'),
1368
- assetFeesToNumeraireBuybackWad: parseEther('0.25'),
1369
- assetFeesToBeneficiaryWad: parseEther('0.25'),
1370
- assetFeesToLpWad: parseEther('0.25'),
1371
- numeraireFeesToAssetBuybackWad: parseEther('0.25'),
1372
- numeraireFeesToNumeraireBuybackWad: parseEther('0.25'),
1373
- numeraireFeesToBeneficiaryWad: parseEther('0.25'),
1374
- numeraireFeesToLpWad: parseEther('0.25'),
1375
- },
1250
+ hook: {
1251
+ hookAddress: '0xYourDopplerHook...',
1252
+ onInitializationCalldata: '0x...',
1376
1253
  },
1377
1254
  })
1378
1255
  .withUserAddress('0xYourAddress...')
1379
1256
  .build();
1380
1257
  ```
1381
1258
 
1382
- Note: `dopplerHookMigrator` beneficiaries must include the current Airlock owner
1383
- with at least 5% shares, and total shares must sum to `1e18`.
1384
- Unlike initializer-side Rehype pools, migrator-side Rehype uses a static
1385
- `customFee`; there is no fee decay schedule in this mode.
1259
+ `dopplerHookMigrator` beneficiaries must include the current Airlock owner with
1260
+ at least 5% shares, and total shares must sum to `1e18`. Omit `hook` for a
1261
+ standard migrated pool without custom hook behavior.
1262
+
1386
1263
  For backwards compatibility, the deprecated `DopplerHookMigrationConfig` type
1387
1264
  and its `type: 'dopplerHook'` discriminator remain accepted. New code should use
1388
1265
  `DopplerHookMigratorConfig` with `type: 'dopplerHookMigrator'`. Multicurve
1389
1266
  initializer params similarly accept the deprecated `type: 'dopplerHook'`
1390
1267
  discriminator, which resolves to `dopplerHookInitializer`.
1391
1268
 
1392
- ```typescript
1393
- migration: {
1394
- type: 'dopplerHookMigrator',
1395
- fee: 3000,
1396
- useDynamicFee: false,
1397
- tickSpacing: 10,
1398
- lockDuration: 30 * 24 * 60 * 60,
1399
- beneficiaries: [
1400
- { beneficiary: '0xYourBeneficiary...', shares: parseEther('1') },
1401
- ],
1402
- rehype: {
1403
- // optional; defaults to chain rehypeDopplerHookMigrator address
1404
- // hookAddress: '0xRehypeMigratorHook...',
1405
- buybackDestination: '0xYourBuybackDestination...',
1406
- customFee: 3000,
1407
- feeRoutingMode: 'directBuyback',
1408
- feeDistributionInfo: {
1409
- assetFeesToAssetBuybackWad: parseEther('0.25'),
1410
- assetFeesToNumeraireBuybackWad: parseEther('0.25'),
1411
- assetFeesToBeneficiaryWad: parseEther('0.25'),
1412
- assetFeesToLpWad: parseEther('0.25'),
1413
- numeraireFeesToAssetBuybackWad: parseEther('0.25'),
1414
- numeraireFeesToNumeraireBuybackWad: parseEther('0.25'),
1415
- numeraireFeesToBeneficiaryWad: parseEther('0.25'),
1416
- numeraireFeesToLpWad: parseEther('0.25'),
1417
- },
1418
- },
1419
- proceedsSplit: {
1420
- recipient: '0xProceedsRecipient...',
1421
- share: parseEther('0.1'),
1422
- },
1423
- }
1424
- ```
1425
-
1426
1269
  To make configuring the first beneficiary simpler, the SDK now exposes helpers for resolving the
1427
1270
  airlock owner and creating the default 5% entry:
1428
1271
 
@@ -1488,12 +1331,9 @@ for (const id of SUPPORTED_CHAIN_IDS) {
1488
1331
  }
1489
1332
  ```
1490
1333
 
1491
- Arbitrum One is available as `CHAIN_IDS.ARBITRUM` (`42161`) with a viem chain
1492
- definition included in `SupportedChain`.
1334
+ Available launch features depend on the contracts deployed on the selected network. Use `getAddresses(chainId)` to inspect its deployment configuration. For native V4 numeraires, use `zeroAddress` and the chain's native currency decimals rather than the decimals of an ERC-20 token with the same symbol.
1493
1335
 
1494
- Robinhood Chain is available as `CHAIN_IDS.ROBINHOOD` (`4663`). The SDK exposes
1495
- addresses and support checks for it, but does not export a viem chain definition;
1496
- use your application's chain/client setup when constructing clients.
1336
+ Arc uses native USDC with 18 decimals. Import the SDK's `arc` chain definition and provide an explicit RPC transport; Multicall3 is configured for fee previews. Arc launches with `numeraire: zeroAddress` must stay on Uniswap V4: all V3 static launches (including `LockableUniswapV3Initializer`) and both `uniswapV2` and `uniswapV2Split` migrations reject native USDC. Use a nonzero ERC-20 numeraire for those V2/V3 paths. For native dynamic auctions on Arc, use `dopplerHookMigrator`; locked native multicurve pools can use `noOp`.
1497
1337
 
1498
1338
  ## Advanced Usage
1499
1339
 
@@ -1850,51 +1690,40 @@ pnpm dev
1850
1690
 
1851
1691
  The SDK includes comprehensive tests covering:
1852
1692
 
1853
- - **Airlock Whitelisting**: Verifies that all modules are properly whitelisted on Ethereum Mainnet, Arbitrum One, Monad Mainnet, Base Mainnet, Base Sepolia, and Robinhood Chain
1854
- - **Multicurve Functionality**: Tests multicurve auction creation and quoting
1693
+ - **Airlock Whitelisting**: Verifies that configured modules are whitelisted on the selected networks
1694
+ - **Auction Workflows**: Tests dynamic and multicurve creation, quoting, and executed buy/sell round trips on local Anvil forks
1855
1695
  - **Token Address Mining**: Tests for generating optimized token addresses
1856
1696
 
1857
- To run whitelisting tests:
1697
+ Configure Alchemy once, then run the whitelist audit:
1858
1698
 
1859
1699
  ```bash
1860
- # Canonical whitelist audit
1700
+ export ALCHEMY_API_KEY=your_key_here
1861
1701
  pnpm test:whitelisting
1862
1702
 
1863
- # With Alchemy fallback (faster and more reliable)
1864
- ALCHEMY_API_KEY=your_key_here pnpm test:whitelisting
1865
-
1866
1703
  # Limit to specific whitelist-audit chains when needed
1867
- TEST_CHAINS=mainnet,base,base-sepolia,arbitrum,monad-mainnet,robinhood pnpm test:whitelisting
1704
+ TEST_CHAINS=mainnet,base,base-sepolia,arbitrum,bsc,arc,monad-mainnet,robinhood pnpm test:whitelisting
1868
1705
  ```
1869
1706
 
1870
- The whitelisting suite is scoped to the release-audit chains: Ethereum Mainnet, Arbitrum One, Monad Mainnet, Base Mainnet, Base Sepolia, and Robinhood Chain.
1707
+ Use `TEST_CHAINS` to select networks by their comma-separated names, or omit it to check all networks configured in the whitelisting suite.
1871
1708
 
1872
- Whitelisting test RPC priority is:
1873
-
1874
- 1. Chain-specific RPC URL env var (`ETH_MAINNET_RPC_URL`, `ARBITRUM_RPC_URL`, `BASE_RPC_URL`, `BASE_SEPOLIA_RPC_URL`)
1875
- 2. `ALCHEMY_API_KEY` fallback for supported Alchemy networks, including Monad Mainnet
1876
- 3. Public/default RPC URL
1709
+ The test harness selects the network endpoint from the chain ID; no per-chain RPC URL setup is needed.
1877
1710
 
1878
1711
  To run fork tests (Anvil):
1879
1712
 
1880
1713
  ```bash
1881
1714
  # all fork tests
1882
- ALCHEMY_API_KEY=your_key_here pnpm test:fork
1715
+ pnpm test:fork
1883
1716
 
1884
1717
  # chain-specific fork tests
1885
- ALCHEMY_API_KEY=your_key_here TEST_CHAIN=base pnpm test:fork
1886
- ALCHEMY_API_KEY=your_key_here TEST_CHAIN=base-sepolia pnpm test:fork
1887
- ALCHEMY_API_KEY=your_key_here TEST_CHAIN=mainnet pnpm test:fork
1888
- ALCHEMY_API_KEY=your_key_here TEST_CHAIN=eth-sepolia pnpm test:fork
1718
+ TEST_CHAIN=base pnpm test:fork
1719
+ TEST_CHAIN=base-sepolia pnpm test:fork
1720
+ TEST_CHAIN=mainnet pnpm test:fork
1721
+ TEST_CHAIN=monad-mainnet pnpm test:fork
1722
+ TEST_CHAIN=robinhood pnpm test:fork
1723
+ TEST_CHAIN=arc pnpm test:fork
1889
1724
  ```
1890
1725
 
1891
- You can also provide chain-specific RPC URLs directly:
1892
-
1893
- ```bash
1894
- ETH_MAINNET_RPC_URL=https://... TEST_CHAIN=mainnet pnpm test:fork
1895
- ARBITRUM_RPC_URL=https://... TEST_CHAIN=arbitrum pnpm test:fork
1896
- ETH_SEPOLIA_RPC_URL=https://... TEST_CHAIN=eth-sepolia pnpm test:fork
1897
- ```
1726
+ The shared mainnet suite buys and partially sells through each newly created dynamic, Rehype, NoOp, and deployed scheduled pool. Arc uses native USDC; other networks use their configured wrapped native token. Checks verify receipts, acquired and sold tokens, and returned numeraire, with gas costs excluded from native sell proceeds. Fork tests do not broadcast to mainnet.
1898
1727
 
1899
1728
  ## Migration from Previous SDKs
1900
1729