@whetstone-research/doppler-sdk 1.0.26 → 1.0.28

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
@@ -444,9 +444,9 @@ console.log('Token address:', presetResult.tokenAddress);
444
444
 
445
445
  The preset helper seeds three curated curve buckets sized for ~1B token supply targets:
446
446
 
447
- - `low`: ~5% of the sale allocated to a $7.5k-$30k market cap window.
448
- - `medium`: ~12.5% targeting roughly $50k-$150k market caps.
449
- - `high`: ~20% aimed at $250k-$750k market caps.
447
+ - `low`: 50% of the sale allocated to a $0-$3M market cap window.
448
+ - `medium`: 25% targeting roughly $1k-$40M market caps.
449
+ - `high`: 24% aimed at $100k-$1B market caps.
450
450
 
451
451
  Pass `presets` to pick a subset (e.g. `['medium', 'high']`) or provide `overrides` to adjust ticks, positions, or shares for a specific tier. When the selected presets sum to less than 100%, the builder automatically appends a filler curve (using the highest selected tier's shape) so liquidity always covers the full sale. Shares must stay within 0-1e18 and the helper will throw if the total ever exceeds 100%.
452
452
 
@@ -914,6 +914,8 @@ import { Derc20 } from '@whetstone-research/doppler-sdk/evm';
914
914
  const tokenDirect = new Derc20(publicClient, walletClient, tokenAddress);
915
915
  ```
916
916
 
917
+ For a runnable release-focused example covering legacy DERC20, DERC20 V2 schedules, and DopplerERC20V1 partial releases, see [examples/vesting-release.ts](./examples/vesting-release.ts).
918
+
917
919
  ### DopplerERC20V1 Tokens
918
920
 
919
921
  Use the newer DopplerERC20V1 token template by either setting `type: 'dopplerERC20V1'` explicitly or by passing fields such as `maxBalanceLimit` with `balanceLimitEnd`, `controller`, or `excludedFromBalanceLimit`. When selected, the SDK uses the configured `dopplerERC20V1Factory` by default. `withTokenFactory(address)` is a generic factory override and takes precedence, but it must point to a factory compatible with the selected token path and token data ABI. `controller` is optional and defaults to the zero address, set it only if early balance-limit disable should be possible. Standard configs without the specific fields still use the legacy `standard` path, where cliff/allocation vesting routes to legacy DERC20 V2. Keep explicit `type: 'dopplerERC20V1'` when you want its behavior but have no specific fields to infer from.
@@ -1427,6 +1429,10 @@ for (const id of SUPPORTED_CHAIN_IDS) {
1427
1429
  }
1428
1430
  ```
1429
1431
 
1432
+ Robinhood Chain is available as `CHAIN_IDS.ROBINHOOD` (`4663`). The SDK exposes
1433
+ addresses and support checks for it, but does not export a viem chain definition;
1434
+ use your application's chain/client setup when constructing clients.
1435
+
1430
1436
  ## Advanced Usage
1431
1437
 
1432
1438
  ### Custom Vesting Configuration
@@ -1782,7 +1788,7 @@ pnpm dev
1782
1788
 
1783
1789
  The SDK includes comprehensive tests covering:
1784
1790
 
1785
- - **Airlock Whitelisting**: Verifies that all modules are properly whitelisted on Ethereum Mainnet, Monad Mainnet, Base Mainnet, and Base Sepolia
1791
+ - **Airlock Whitelisting**: Verifies that all modules are properly whitelisted on Ethereum Mainnet, Monad Mainnet, Base Mainnet, Base Sepolia, and Robinhood Chain
1786
1792
  - **Multicurve Functionality**: Tests multicurve auction creation and quoting
1787
1793
  - **Token Address Mining**: Tests for generating optimized token addresses
1788
1794
 
@@ -1796,10 +1802,10 @@ pnpm test:whitelisting
1796
1802
  ALCHEMY_API_KEY=your_key_here pnpm test:whitelisting
1797
1803
 
1798
1804
  # Limit to specific whitelist-audit chains when needed
1799
- TEST_CHAINS=mainnet,base,base-sepolia,monad-mainnet pnpm test:whitelisting
1805
+ TEST_CHAINS=mainnet,base,base-sepolia,monad-mainnet,robinhood pnpm test:whitelisting
1800
1806
  ```
1801
1807
 
1802
- The whitelisting suite is scoped to the release-audit chains: Ethereum Mainnet, Monad Mainnet, Base Mainnet, and Base Sepolia.
1808
+ The whitelisting suite is scoped to the release-audit chains: Ethereum Mainnet, Monad Mainnet, Base Mainnet, Base Sepolia, and Robinhood Chain.
1803
1809
 
1804
1810
  Whitelisting test RPC priority is:
1805
1811
 
@@ -1833,7 +1839,7 @@ If you're migrating from `doppler-v3-sdk` or `doppler-v4-sdk`, see our [Migratio
1833
1839
 
1834
1840
  ## Contributing
1835
1841
 
1836
- Contributions are welcome! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details.
1842
+ Contributions are welcome! Please see our [Contributing Guide](./CONTRIBUTING.md) for details.
1837
1843
 
1838
1844
  ## License
1839
1845
 
@@ -264,6 +264,85 @@ function encodeTransferAdminArgs(args) {
264
264
  function encodeOracleConsultArgs(args) {
265
265
  return new Uint8Array(oracleConsultArgsCodec.encode(args));
266
266
  }
267
+ function isObject(value) {
268
+ return typeof value === "object" && value !== null;
269
+ }
270
+ function isTransactionSigner(value) {
271
+ return isObject(value) && "address" in value && "signTransactions" in value;
272
+ }
273
+ function getAddressFromAddressOrSigner(value) {
274
+ return isTransactionSigner(value) ? value.address : value;
275
+ }
276
+ function getAddressFromRemainingAccount(account) {
277
+ if (typeof account === "string") {
278
+ return account;
279
+ }
280
+ return account.address;
281
+ }
282
+ function createAccountMeta(value, role) {
283
+ if (isTransactionSigner(value)) {
284
+ return { address: value.address, role, signer: value };
285
+ }
286
+ return { address: value, role };
287
+ }
288
+ function createReadonlyRemainingAccountMeta(account) {
289
+ if (typeof account === "string") {
290
+ return { address: account, role: kit.AccountRole.READONLY };
291
+ }
292
+ if (isTransactionSigner(account)) {
293
+ return {
294
+ address: account.address,
295
+ role: kit.AccountRole.READONLY_SIGNER,
296
+ signer: account
297
+ };
298
+ }
299
+ return account;
300
+ }
301
+ function bytesToBase64(bytes) {
302
+ let binary = "";
303
+ for (let i = 0; i < bytes.length; i++) {
304
+ binary += String.fromCharCode(bytes[i]);
305
+ }
306
+ return btoa(binary);
307
+ }
308
+ function bytesToBase64EncodedBytes(bytes) {
309
+ return bytesToBase64(bytes);
310
+ }
311
+ function base64ToBytes(base64) {
312
+ const binary = atob(base64);
313
+ const bytes = new Uint8Array(binary.length);
314
+ for (let i = 0; i < binary.length; i++) {
315
+ bytes[i] = binary.charCodeAt(i);
316
+ }
317
+ return bytes;
318
+ }
319
+ function addressToBase58EncodedBytes(address) {
320
+ return address;
321
+ }
322
+ function isEncodedProgramAccount(account) {
323
+ if (!isObject(account) || typeof account.pubkey !== "string") {
324
+ return false;
325
+ }
326
+ const accountData = account.account;
327
+ if (!isObject(accountData) || !Array.isArray(accountData.data)) {
328
+ return false;
329
+ }
330
+ const [encodedData, encoding] = accountData.data;
331
+ return typeof encodedData === "string" && encoding === "base64";
332
+ }
333
+ function normalizeProgramAccountsResponse(response) {
334
+ const accounts = Array.isArray(response) ? response : isObject(response) && Array.isArray(response.value) ? response.value : null;
335
+ if (!accounts) {
336
+ throw new Error("Unexpected getProgramAccounts response shape");
337
+ }
338
+ if (!accounts.every(isEncodedProgramAccount)) {
339
+ throw new Error("Unexpected getProgramAccounts account shape");
340
+ }
341
+ return accounts;
342
+ }
343
+ function warnAccountDecodeFailure(accountType, accountAddress) {
344
+ console.warn(`Failed to decode ${accountType} account: ${accountAddress}`);
345
+ }
267
346
 
268
347
  // src/solana/core/math.ts
269
348
  function q64ToNumber(q64) {
@@ -3352,21 +3431,6 @@ function parseAddLiquidityInstruction(instruction) {
3352
3431
  }
3353
3432
 
3354
3433
  // src/solana/client/pool.ts
3355
- function bytesToBase64(bytes) {
3356
- let binary = "";
3357
- for (let i = 0; i < bytes.length; i++) {
3358
- binary += String.fromCharCode(bytes[i]);
3359
- }
3360
- return btoa(binary);
3361
- }
3362
- function base64ToBytes(base64) {
3363
- const binary = atob(base64);
3364
- const bytes = new Uint8Array(binary.length);
3365
- for (let i = 0; i < binary.length; i++) {
3366
- bytes[i] = binary.charCodeAt(i);
3367
- }
3368
- return bytes;
3369
- }
3370
3434
  async function fetchPool(rpc, address, config) {
3371
3435
  const response = await rpc.getAccountInfo(address, {
3372
3436
  encoding: "base64",
@@ -3382,7 +3446,7 @@ async function fetchAllPools(rpc, config) {
3382
3446
  const discriminatorFilter = {
3383
3447
  memcmp: {
3384
3448
  offset: 0n,
3385
- bytes: bytesToBase64(chunkU6B52TBT_cjs.ACCOUNT_DISCRIMINATORS.Pool),
3449
+ bytes: bytesToBase64EncodedBytes(chunkU6B52TBT_cjs.ACCOUNT_DISCRIMINATORS.Pool),
3386
3450
  encoding: "base64"
3387
3451
  }
3388
3452
  };
@@ -3391,7 +3455,7 @@ async function fetchAllPools(rpc, config) {
3391
3455
  commitment: config?.commitment,
3392
3456
  filters: [discriminatorFilter]
3393
3457
  }).send();
3394
- const accounts = Array.isArray(response) ? response : response.value;
3458
+ const accounts = normalizeProgramAccountsResponse(response);
3395
3459
  const pools = [];
3396
3460
  for (const account of accounts) {
3397
3461
  try {
@@ -3401,7 +3465,7 @@ async function fetchAllPools(rpc, config) {
3401
3465
  account: pool
3402
3466
  });
3403
3467
  } catch {
3404
- console.warn(`Failed to decode pool account: ${account.pubkey}`);
3468
+ warnAccountDecodeFailure("pool", account.pubkey);
3405
3469
  }
3406
3470
  }
3407
3471
  return pools;
@@ -3459,21 +3523,6 @@ function sortPoolsByReserves(pools, descending = true) {
3459
3523
  }
3460
3524
 
3461
3525
  // src/solana/client/position.ts
3462
- function bytesToBase642(bytes) {
3463
- let binary = "";
3464
- for (let i = 0; i < bytes.length; i++) {
3465
- binary += String.fromCharCode(bytes[i]);
3466
- }
3467
- return btoa(binary);
3468
- }
3469
- function base64ToBytes2(base64) {
3470
- const binary = atob(base64);
3471
- const bytes = new Uint8Array(binary.length);
3472
- for (let i = 0; i < binary.length; i++) {
3473
- bytes[i] = binary.charCodeAt(i);
3474
- }
3475
- return bytes;
3476
- }
3477
3526
  async function fetchPosition(rpc, address, config) {
3478
3527
  const response = await rpc.getAccountInfo(address, {
3479
3528
  encoding: "base64",
@@ -3482,7 +3531,7 @@ async function fetchPosition(rpc, address, config) {
3482
3531
  if (!response.value) {
3483
3532
  return null;
3484
3533
  }
3485
- return decodePosition(base64ToBytes2(response.value.data[0]));
3534
+ return decodePosition(base64ToBytes(response.value.data[0]));
3486
3535
  }
3487
3536
  async function fetchUserPositions(rpc, owner, pool, config) {
3488
3537
  const programId = config?.programId ?? chunkU6B52TBT_cjs.CPMM_PROGRAM_ID;
@@ -3491,9 +3540,7 @@ async function fetchUserPositions(rpc, owner, pool, config) {
3491
3540
  {
3492
3541
  memcmp: {
3493
3542
  offset: 0n,
3494
- bytes: bytesToBase642(
3495
- chunkU6B52TBT_cjs.ACCOUNT_DISCRIMINATORS.Position
3496
- ),
3543
+ bytes: bytesToBase64EncodedBytes(chunkU6B52TBT_cjs.ACCOUNT_DISCRIMINATORS.Position),
3497
3544
  encoding: "base64"
3498
3545
  }
3499
3546
  },
@@ -3501,7 +3548,7 @@ async function fetchUserPositions(rpc, owner, pool, config) {
3501
3548
  {
3502
3549
  memcmp: {
3503
3550
  offset: 40n,
3504
- bytes: owner,
3551
+ bytes: addressToBase58EncodedBytes(owner),
3505
3552
  encoding: "base58"
3506
3553
  }
3507
3554
  }
@@ -3510,7 +3557,7 @@ async function fetchUserPositions(rpc, owner, pool, config) {
3510
3557
  filters.push({
3511
3558
  memcmp: {
3512
3559
  offset: 8n,
3513
- bytes: pool,
3560
+ bytes: addressToBase58EncodedBytes(pool),
3514
3561
  encoding: "base58"
3515
3562
  }
3516
3563
  });
@@ -3520,17 +3567,17 @@ async function fetchUserPositions(rpc, owner, pool, config) {
3520
3567
  commitment: config?.commitment,
3521
3568
  filters
3522
3569
  }).send();
3523
- const accounts = Array.isArray(response) ? response : response.value;
3570
+ const accounts = normalizeProgramAccountsResponse(response);
3524
3571
  const positions = [];
3525
3572
  for (const account of accounts) {
3526
3573
  try {
3527
- const position = decodePosition(base64ToBytes2(account.account.data[0]));
3574
+ const position = decodePosition(base64ToBytes(account.account.data[0]));
3528
3575
  positions.push({
3529
3576
  address: account.pubkey,
3530
3577
  account: position
3531
3578
  });
3532
3579
  } catch {
3533
- console.warn(`Failed to decode position account: ${account.pubkey}`);
3580
+ warnAccountDecodeFailure("position", account.pubkey);
3534
3581
  }
3535
3582
  }
3536
3583
  return positions;
@@ -3542,9 +3589,7 @@ async function fetchPoolPositions(rpc, pool, config) {
3542
3589
  {
3543
3590
  memcmp: {
3544
3591
  offset: 0n,
3545
- bytes: bytesToBase642(
3546
- chunkU6B52TBT_cjs.ACCOUNT_DISCRIMINATORS.Position
3547
- ),
3592
+ bytes: bytesToBase64EncodedBytes(chunkU6B52TBT_cjs.ACCOUNT_DISCRIMINATORS.Position),
3548
3593
  encoding: "base64"
3549
3594
  }
3550
3595
  },
@@ -3552,7 +3597,7 @@ async function fetchPoolPositions(rpc, pool, config) {
3552
3597
  {
3553
3598
  memcmp: {
3554
3599
  offset: 8n,
3555
- bytes: pool,
3600
+ bytes: addressToBase58EncodedBytes(pool),
3556
3601
  encoding: "base58"
3557
3602
  }
3558
3603
  }
@@ -3562,17 +3607,17 @@ async function fetchPoolPositions(rpc, pool, config) {
3562
3607
  commitment: config?.commitment,
3563
3608
  filters
3564
3609
  }).send();
3565
- const accounts = Array.isArray(response) ? response : response.value;
3610
+ const accounts = normalizeProgramAccountsResponse(response);
3566
3611
  const positions = [];
3567
3612
  for (const account of accounts) {
3568
3613
  try {
3569
- const position = decodePosition(base64ToBytes2(account.account.data[0]));
3614
+ const position = decodePosition(base64ToBytes(account.account.data[0]));
3570
3615
  positions.push({
3571
3616
  address: account.pubkey,
3572
3617
  account: position
3573
3618
  });
3574
3619
  } catch {
3575
- console.warn(`Failed to decode position account: ${account.pubkey}`);
3620
+ warnAccountDecodeFailure("position", account.pubkey);
3576
3621
  }
3577
3622
  }
3578
3623
  return positions;
@@ -3653,14 +3698,6 @@ function sortPositionsByShares(positions, descending = true) {
3653
3698
  }
3654
3699
 
3655
3700
  // src/solana/client/oracle.ts
3656
- function base64ToBytes3(base64) {
3657
- const binary = atob(base64);
3658
- const bytes = new Uint8Array(binary.length);
3659
- for (let i = 0; i < binary.length; i++) {
3660
- bytes[i] = binary.charCodeAt(i);
3661
- }
3662
- return bytes;
3663
- }
3664
3701
  async function fetchOracle(rpc, address, config) {
3665
3702
  const response = await rpc.getAccountInfo(address, {
3666
3703
  encoding: "base64",
@@ -3669,7 +3706,7 @@ async function fetchOracle(rpc, address, config) {
3669
3706
  if (!response.value) {
3670
3707
  return null;
3671
3708
  }
3672
- return decodeOracleState(base64ToBytes3(response.value.data[0]));
3709
+ return decodeOracleState(base64ToBytes(response.value.data[0]));
3673
3710
  }
3674
3711
  async function getOracleForPool(rpc, pool, config) {
3675
3712
  const programId = config?.programId ?? chunkU6B52TBT_cjs.CPMM_PROGRAM_ID;
@@ -3860,7 +3897,11 @@ exports.UNPAUSE_DISCRIMINATOR = UNPAUSE_DISCRIMINATOR;
3860
3897
  exports.UPDATE_CONFIG_DISCRIMINATOR = UPDATE_CONFIG_DISCRIMINATOR;
3861
3898
  exports.WITHDRAW_VAULT_EXCESS_DISCRIMINATOR = WITHDRAW_VAULT_EXCESS_DISCRIMINATOR;
3862
3899
  exports.addLiquidityArgsCodec = addLiquidityArgsCodec;
3900
+ exports.addressToBase58EncodedBytes = addressToBase58EncodedBytes;
3863
3901
  exports.ammConfigDataCodec = ammConfigDataCodec;
3902
+ exports.base64ToBytes = base64ToBytes;
3903
+ exports.bytesToBase64 = bytesToBase64;
3904
+ exports.bytesToBase64EncodedBytes = bytesToBase64EncodedBytes;
3864
3905
  exports.calculateAccruedFees = calculateAccruedFees;
3865
3906
  exports.calculateTwap = calculateTwap;
3866
3907
  exports.calculateTwapNumber = calculateTwapNumber;
@@ -3871,7 +3912,9 @@ exports.comparePoolAndOraclePrices = comparePoolAndOraclePrices;
3871
3912
  exports.computePrice0Q64 = computePrice0Q64;
3872
3913
  exports.computePrice1Q64 = computePrice1Q64;
3873
3914
  exports.consultTwap = consultTwap;
3915
+ exports.createAccountMeta = createAccountMeta;
3874
3916
  exports.createPositionArgsCodec = createPositionArgsCodec;
3917
+ exports.createReadonlyRemainingAccountMeta = createReadonlyRemainingAccountMeta;
3875
3918
  exports.decodeAmmConfig = decodeAmmConfig;
3876
3919
  exports.decodeOracleState = decodeOracleState;
3877
3920
  exports.decodePool = decodePool;
@@ -3909,6 +3952,8 @@ exports.getAddLiquidityInstructionDataCodec = getAddLiquidityInstructionDataCode
3909
3952
  exports.getAddLiquidityInstructionDataDecoder = getAddLiquidityInstructionDataDecoder;
3910
3953
  exports.getAddLiquidityInstructionDataEncoder = getAddLiquidityInstructionDataEncoder;
3911
3954
  exports.getAddLiquidityQuote = getAddLiquidityQuote;
3955
+ exports.getAddressFromAddressOrSigner = getAddressFromAddressOrSigner;
3956
+ exports.getAddressFromRemainingAccount = getAddressFromRemainingAccount;
3912
3957
  exports.getClosePositionDiscriminatorBytes = getClosePositionDiscriminatorBytes;
3913
3958
  exports.getClosePositionInstruction = getClosePositionInstruction;
3914
3959
  exports.getClosePositionInstructionDataCodec = getClosePositionInstructionDataCodec;
@@ -4043,9 +4088,11 @@ exports.initializeConfigArgsCodec = initializeConfigArgsCodec;
4043
4088
  exports.initializeOracleArgsCodec = initializeOracleArgsCodec;
4044
4089
  exports.initializePoolArgsCodec = initializePoolArgsCodec;
4045
4090
  exports.isOracleStale = isOracleStale;
4091
+ exports.isTransactionSigner = isTransactionSigner;
4046
4092
  exports.isqrt = isqrt;
4047
4093
  exports.maxBigInt = maxBigInt;
4048
4094
  exports.minBigInt = minBigInt;
4095
+ exports.normalizeProgramAccountsResponse = normalizeProgramAccountsResponse;
4049
4096
  exports.numberToQ64 = numberToQ64;
4050
4097
  exports.observationCodec = observationCodec;
4051
4098
  exports.oracleConsultArgsCodec = oracleConsultArgsCodec;
@@ -4085,5 +4132,6 @@ exports.sortPoolsByReserves = sortPoolsByReserves;
4085
4132
  exports.sortPositionsByShares = sortPositionsByShares;
4086
4133
  exports.swapExactInArgsCodec = swapExactInArgsCodec;
4087
4134
  exports.transferAdminArgsCodec = transferAdminArgsCodec;
4088
- //# sourceMappingURL=chunk-XTL2GBZ4.cjs.map
4089
- //# sourceMappingURL=chunk-XTL2GBZ4.cjs.map
4135
+ exports.warnAccountDecodeFailure = warnAccountDecodeFailure;
4136
+ //# sourceMappingURL=chunk-4WY5GNZD.cjs.map
4137
+ //# sourceMappingURL=chunk-4WY5GNZD.cjs.map