@whetstone-research/doppler-sdk 1.0.5 → 1.0.7
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 +16 -1
- package/dist/evm/index.cjs +204 -25
- package/dist/evm/index.cjs.map +1 -1
- package/dist/evm/index.d.cts +49 -33
- package/dist/evm/index.d.ts +49 -33
- package/dist/evm/index.js +204 -25
- package/dist/evm/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -88,6 +88,19 @@ const params = new StaticAuctionBuilder()
|
|
|
88
88
|
// Optional: specify multiple recipients and amounts
|
|
89
89
|
// recipients: ['0xTeam...', '0xAdvisor...'],
|
|
90
90
|
// amounts: [parseEther('50000000'), parseEther('50000000')]
|
|
91
|
+
// Optional: define per-beneficiary vesting allocations on the DERC20 V2 path
|
|
92
|
+
// allocations: [
|
|
93
|
+
// {
|
|
94
|
+
// recipient: '0xTeam...',
|
|
95
|
+
// amount: parseEther('50000000'),
|
|
96
|
+
// schedule: { duration: BigInt(180 * 24 * 60 * 60), cliffDuration: 30 * 24 * 60 * 60 },
|
|
97
|
+
// },
|
|
98
|
+
// {
|
|
99
|
+
// recipient: '0xAdvisor...',
|
|
100
|
+
// amount: parseEther('50000000'),
|
|
101
|
+
// schedule: { duration: BigInt(365 * 24 * 60 * 60), cliffDuration: 90 * 24 * 60 * 60 },
|
|
102
|
+
// },
|
|
103
|
+
// ]
|
|
91
104
|
})
|
|
92
105
|
.withMigration({ type: 'uniswapV2' })
|
|
93
106
|
.withUserAddress('0x...')
|
|
@@ -98,7 +111,9 @@ console.log('Pool address:', result.poolAddress);
|
|
|
98
111
|
console.log('Token address:', result.tokenAddress);
|
|
99
112
|
```
|
|
100
113
|
|
|
101
|
-
If you set `cliffDuration > 0`, the SDK
|
|
114
|
+
If you set `cliffDuration > 0` or provide `allocations`, the SDK automatically uses the DERC20 V2 factory and exposes schedule-aware token reads via `sdk.getDerc20V2(tokenAddress)`. When `allocations` is provided, the SDK dedupes identical schedules internally and maps each recipient to the correct on-chain schedule.
|
|
115
|
+
|
|
116
|
+
For a runnable example, see [examples/multicurve-per-beneficiary-vesting.ts](./examples/multicurve-per-beneficiary-vesting.ts).
|
|
102
117
|
|
|
103
118
|
> **Tick spacing reminder:** When you provide ticks manually via `poolByTicks`, make sure both `startTick` and `endTick` are exact multiples of the fee tier's tick spacing (100→1, 500→10, 3000→60, 10000→200). The SDK now validates this locally and will fail fast if the ticks are misaligned.
|
|
104
119
|
|
package/dist/evm/index.cjs
CHANGED
|
@@ -5282,6 +5282,7 @@ var erc20BalanceOfAbi = [
|
|
|
5282
5282
|
];
|
|
5283
5283
|
var TOKEN_FACTORY_80_ADDRESS2 = "0xf0b5141dd9096254b2ca624dff26024f46087229";
|
|
5284
5284
|
var DERC20_V2_MIN_VESTING_DURATION = 24 * 60 * 60;
|
|
5285
|
+
var MAX_UINT64 = (1n << 64n) - 1n;
|
|
5285
5286
|
var DopplerFactory = class {
|
|
5286
5287
|
publicClient;
|
|
5287
5288
|
walletClient;
|
|
@@ -5293,13 +5294,29 @@ var DopplerFactory = class {
|
|
|
5293
5294
|
this.walletClient = walletClient;
|
|
5294
5295
|
this.chainId = chainId;
|
|
5295
5296
|
}
|
|
5297
|
+
hasCustomV2Schedules(vesting) {
|
|
5298
|
+
return (vesting?.allocations?.length ?? 0) > 0;
|
|
5299
|
+
}
|
|
5296
5300
|
usesDerc20V2Vesting(vesting) {
|
|
5297
|
-
|
|
5301
|
+
if (!vesting) {
|
|
5302
|
+
return false;
|
|
5303
|
+
}
|
|
5304
|
+
return this.hasCustomV2Schedules(vesting) || (vesting.cliffDuration ?? 0) > 0;
|
|
5298
5305
|
}
|
|
5299
5306
|
resolveVestingAllocations(args) {
|
|
5300
5307
|
if (!args.vesting) {
|
|
5301
5308
|
return { recipients: [], amounts: [] };
|
|
5302
5309
|
}
|
|
5310
|
+
if (args.vesting.allocations) {
|
|
5311
|
+
return {
|
|
5312
|
+
recipients: args.vesting.allocations.map(
|
|
5313
|
+
(allocation) => allocation.recipient
|
|
5314
|
+
),
|
|
5315
|
+
amounts: args.vesting.allocations.map(
|
|
5316
|
+
(allocation) => allocation.amount
|
|
5317
|
+
)
|
|
5318
|
+
};
|
|
5319
|
+
}
|
|
5303
5320
|
if (args.vesting.recipients && args.vesting.amounts) {
|
|
5304
5321
|
return {
|
|
5305
5322
|
recipients: args.vesting.recipients,
|
|
@@ -5311,6 +5328,56 @@ var DopplerFactory = class {
|
|
|
5311
5328
|
amounts: [args.sale.initialSupply - args.sale.numTokensToSell]
|
|
5312
5329
|
};
|
|
5313
5330
|
}
|
|
5331
|
+
validateUint64LikeNumber(value, fieldPath, options = {}) {
|
|
5332
|
+
const { allowZero = true } = options;
|
|
5333
|
+
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
5334
|
+
throw new Error(`${fieldPath} must be a finite integer`);
|
|
5335
|
+
}
|
|
5336
|
+
if (!Number.isSafeInteger(value)) {
|
|
5337
|
+
throw new Error(`${fieldPath} must be a safe integer`);
|
|
5338
|
+
}
|
|
5339
|
+
if (value < 0) {
|
|
5340
|
+
throw new Error(`${fieldPath} cannot be negative`);
|
|
5341
|
+
}
|
|
5342
|
+
if (!allowZero && value === 0) {
|
|
5343
|
+
throw new Error(`${fieldPath} must be greater than zero`);
|
|
5344
|
+
}
|
|
5345
|
+
if (BigInt(value) > MAX_UINT64) {
|
|
5346
|
+
throw new Error(`${fieldPath} must fit in uint64`);
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
resolveV2VestingSchedules(args) {
|
|
5350
|
+
if (!args.vesting) {
|
|
5351
|
+
return { schedules: [], scheduleIds: [] };
|
|
5352
|
+
}
|
|
5353
|
+
if (!this.hasCustomV2Schedules(args.vesting)) {
|
|
5354
|
+
return {
|
|
5355
|
+
schedules: [
|
|
5356
|
+
{
|
|
5357
|
+
cliff: BigInt(args.vesting.cliffDuration ?? 0),
|
|
5358
|
+
duration: BigInt(args.vesting.duration ?? 0)
|
|
5359
|
+
}
|
|
5360
|
+
],
|
|
5361
|
+
scheduleIds: Array.from({ length: args.recipientCount }, () => 0n)
|
|
5362
|
+
};
|
|
5363
|
+
}
|
|
5364
|
+
const schedules = [];
|
|
5365
|
+
const scheduleIds = [];
|
|
5366
|
+
const scheduleIdsByKey = /* @__PURE__ */ new Map();
|
|
5367
|
+
for (const allocation of args.vesting.allocations ?? []) {
|
|
5368
|
+
const cliff = BigInt(allocation.schedule.cliffDuration ?? 0);
|
|
5369
|
+
const duration = BigInt(allocation.schedule.duration ?? 0);
|
|
5370
|
+
const key = `${cliff}:${duration}`;
|
|
5371
|
+
let scheduleId = scheduleIdsByKey.get(key);
|
|
5372
|
+
if (scheduleId === void 0) {
|
|
5373
|
+
scheduleId = BigInt(schedules.length);
|
|
5374
|
+
schedules.push({ cliff, duration });
|
|
5375
|
+
scheduleIdsByKey.set(key, scheduleId);
|
|
5376
|
+
}
|
|
5377
|
+
scheduleIds.push(scheduleId);
|
|
5378
|
+
}
|
|
5379
|
+
return { schedules, scheduleIds };
|
|
5380
|
+
}
|
|
5314
5381
|
resolveStandardTokenFactoryMode(args) {
|
|
5315
5382
|
if (this.usesDerc20V2Vesting(args.vesting)) {
|
|
5316
5383
|
return "v2";
|
|
@@ -5352,12 +5419,10 @@ var DopplerFactory = class {
|
|
|
5352
5419
|
"DERC20 V2 implementation address not configured for this chain."
|
|
5353
5420
|
);
|
|
5354
5421
|
}
|
|
5355
|
-
const schedules =
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
}
|
|
5360
|
-
];
|
|
5422
|
+
const { schedules, scheduleIds } = this.resolveV2VestingSchedules({
|
|
5423
|
+
vesting: args.vesting,
|
|
5424
|
+
recipientCount: recipients.length
|
|
5425
|
+
});
|
|
5361
5426
|
return {
|
|
5362
5427
|
kind: "v2",
|
|
5363
5428
|
name: args.token.name,
|
|
@@ -5367,7 +5432,7 @@ var DopplerFactory = class {
|
|
|
5367
5432
|
yearlyMintRate,
|
|
5368
5433
|
schedules,
|
|
5369
5434
|
beneficiaries: recipients,
|
|
5370
|
-
scheduleIds
|
|
5435
|
+
scheduleIds,
|
|
5371
5436
|
amounts,
|
|
5372
5437
|
tokenURI: args.token.tokenURI,
|
|
5373
5438
|
implementation
|
|
@@ -8154,19 +8219,49 @@ var DopplerFactory = class {
|
|
|
8154
8219
|
}
|
|
8155
8220
|
const cliffDuration = vesting.cliffDuration ?? 0;
|
|
8156
8221
|
const duration = vesting.duration ?? 0;
|
|
8157
|
-
|
|
8158
|
-
|
|
8159
|
-
}
|
|
8160
|
-
if (duration < 0) {
|
|
8161
|
-
throw new Error("Vesting duration cannot be negative");
|
|
8162
|
-
}
|
|
8163
|
-
if (cliffDuration > duration) {
|
|
8164
|
-
throw new Error("Vesting cliff duration cannot exceed vesting duration");
|
|
8165
|
-
}
|
|
8166
|
-
if (cliffDuration > 0 && duration > 0 && duration < DERC20_V2_MIN_VESTING_DURATION) {
|
|
8222
|
+
const hasCustomSchedules = this.hasCustomV2Schedules(vesting);
|
|
8223
|
+
if (hasCustomSchedules && (cliffDuration > 0 || duration > 0 || vesting.recipients !== void 0 || vesting.amounts !== void 0)) {
|
|
8167
8224
|
throw new Error(
|
|
8168
|
-
|
|
8225
|
+
"Use vesting.allocations instead of top-level duration/cliffDuration/recipients/amounts when configuring per-beneficiary vesting"
|
|
8226
|
+
);
|
|
8227
|
+
}
|
|
8228
|
+
const availableForVesting = sale.initialSupply - sale.numTokensToSell;
|
|
8229
|
+
if (vesting.allocations) {
|
|
8230
|
+
if (vesting.allocations.length === 0) {
|
|
8231
|
+
throw new Error("Vesting allocations array cannot be empty");
|
|
8232
|
+
}
|
|
8233
|
+
const totalVested = vesting.allocations.reduce(
|
|
8234
|
+
(sum, allocation) => sum + allocation.amount,
|
|
8235
|
+
0n
|
|
8169
8236
|
);
|
|
8237
|
+
if (totalVested > availableForVesting) {
|
|
8238
|
+
throw new Error(
|
|
8239
|
+
`Total vesting amount (${totalVested}) exceeds available tokens (${availableForVesting})`
|
|
8240
|
+
);
|
|
8241
|
+
}
|
|
8242
|
+
for (const [index, allocation] of vesting.allocations.entries()) {
|
|
8243
|
+
const scheduleCliff = allocation.schedule.cliffDuration ?? 0;
|
|
8244
|
+
const scheduleDuration = allocation.schedule.duration ?? 0;
|
|
8245
|
+
this.validateUint64LikeNumber(
|
|
8246
|
+
scheduleCliff,
|
|
8247
|
+
`Vesting allocations[${index}].schedule.cliffDuration`
|
|
8248
|
+
);
|
|
8249
|
+
this.validateUint64LikeNumber(
|
|
8250
|
+
scheduleDuration,
|
|
8251
|
+
`Vesting allocations[${index}].schedule.duration`
|
|
8252
|
+
);
|
|
8253
|
+
if (scheduleCliff > scheduleDuration) {
|
|
8254
|
+
throw new Error(
|
|
8255
|
+
`Vesting allocations[${index}].schedule.cliffDuration cannot exceed duration`
|
|
8256
|
+
);
|
|
8257
|
+
}
|
|
8258
|
+
if (scheduleCliff > 0 && scheduleDuration > 0 && scheduleDuration < DERC20_V2_MIN_VESTING_DURATION) {
|
|
8259
|
+
throw new Error(
|
|
8260
|
+
`Vesting allocations[${index}].schedule.duration must be 0 or at least ${DERC20_V2_MIN_VESTING_DURATION} seconds when using cliffs`
|
|
8261
|
+
);
|
|
8262
|
+
}
|
|
8263
|
+
}
|
|
8264
|
+
return;
|
|
8170
8265
|
}
|
|
8171
8266
|
if (vesting.recipients && vesting.amounts) {
|
|
8172
8267
|
if (vesting.recipients.length !== vesting.amounts.length) {
|
|
@@ -8178,17 +8273,30 @@ var DopplerFactory = class {
|
|
|
8178
8273
|
throw new Error("Vesting recipients array cannot be empty");
|
|
8179
8274
|
}
|
|
8180
8275
|
const totalVested = vesting.amounts.reduce((sum, amt) => sum + amt, 0n);
|
|
8181
|
-
const availableForVesting = sale.initialSupply - sale.numTokensToSell;
|
|
8182
8276
|
if (totalVested > availableForVesting) {
|
|
8183
8277
|
throw new Error(
|
|
8184
8278
|
`Total vesting amount (${totalVested}) exceeds available tokens (${availableForVesting})`
|
|
8185
8279
|
);
|
|
8186
8280
|
}
|
|
8187
|
-
|
|
8281
|
+
} else {
|
|
8282
|
+
const vestedAmount = sale.initialSupply - sale.numTokensToSell;
|
|
8283
|
+
if (vestedAmount <= 0n) {
|
|
8284
|
+
throw new Error("No tokens available for vesting");
|
|
8285
|
+
}
|
|
8188
8286
|
}
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8287
|
+
if (cliffDuration < 0) {
|
|
8288
|
+
throw new Error("Vesting cliff duration cannot be negative");
|
|
8289
|
+
}
|
|
8290
|
+
if (duration < 0) {
|
|
8291
|
+
throw new Error("Vesting duration cannot be negative");
|
|
8292
|
+
}
|
|
8293
|
+
if (cliffDuration > duration) {
|
|
8294
|
+
throw new Error("Vesting cliff duration cannot exceed vesting duration");
|
|
8295
|
+
}
|
|
8296
|
+
if (cliffDuration > 0 && duration > 0 && duration < DERC20_V2_MIN_VESTING_DURATION) {
|
|
8297
|
+
throw new Error(
|
|
8298
|
+
`Vesting duration must be 0 or at least ${DERC20_V2_MIN_VESTING_DURATION} seconds when using cliffs`
|
|
8299
|
+
);
|
|
8192
8300
|
}
|
|
8193
8301
|
}
|
|
8194
8302
|
validateStaticAuctionParams(params) {
|
|
@@ -8229,7 +8337,6 @@ var DopplerFactory = class {
|
|
|
8229
8337
|
throw new Error("Cannot sell more tokens than initial supply");
|
|
8230
8338
|
}
|
|
8231
8339
|
this.validateVestingConfig(params.sale, params.vesting);
|
|
8232
|
-
this.validateVestingConfig(params.sale, params.vesting);
|
|
8233
8340
|
if (params.migration.type === "dopplerHook") {
|
|
8234
8341
|
throw new Error(
|
|
8235
8342
|
"dopplerHook migration is only supported for dynamic auctions"
|
|
@@ -13424,6 +13531,26 @@ var Eth = class {
|
|
|
13424
13531
|
};
|
|
13425
13532
|
|
|
13426
13533
|
// src/evm/builders/shared.ts
|
|
13534
|
+
var MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);
|
|
13535
|
+
function normalizeBuilderVestingScheduleDuration(value, fieldPath) {
|
|
13536
|
+
const duration = value ?? 0n;
|
|
13537
|
+
if (duration < 0n) {
|
|
13538
|
+
throw new RangeError(`${fieldPath} cannot be negative`);
|
|
13539
|
+
}
|
|
13540
|
+
if (duration > MAX_SAFE_INTEGER_BIGINT) {
|
|
13541
|
+
throw new RangeError(`${fieldPath} must be a safe integer`);
|
|
13542
|
+
}
|
|
13543
|
+
return Number(duration);
|
|
13544
|
+
}
|
|
13545
|
+
function normalizeBuilderVestingSchedule(schedule, fieldPath) {
|
|
13546
|
+
return {
|
|
13547
|
+
duration: normalizeBuilderVestingScheduleDuration(
|
|
13548
|
+
schedule.duration,
|
|
13549
|
+
`${fieldPath}.duration`
|
|
13550
|
+
),
|
|
13551
|
+
cliffDuration: schedule.cliffDuration ?? 0
|
|
13552
|
+
};
|
|
13553
|
+
}
|
|
13427
13554
|
function computeTicks(priceRange, tickSpacing) {
|
|
13428
13555
|
const startTick = Math.floor(
|
|
13429
13556
|
Math.log(priceRange.startPrice) / Math.log(1.0001) / tickSpacing
|
|
@@ -13719,6 +13846,19 @@ var StaticAuctionBuilder = class _StaticAuctionBuilder {
|
|
|
13719
13846
|
this.vesting = void 0;
|
|
13720
13847
|
return this;
|
|
13721
13848
|
}
|
|
13849
|
+
if (params.allocations) {
|
|
13850
|
+
this.vesting = {
|
|
13851
|
+
allocations: params.allocations.map((allocation, index) => ({
|
|
13852
|
+
recipient: allocation.recipient,
|
|
13853
|
+
amount: allocation.amount,
|
|
13854
|
+
schedule: normalizeBuilderVestingSchedule(
|
|
13855
|
+
allocation.schedule,
|
|
13856
|
+
`Vesting allocations[${index}].schedule`
|
|
13857
|
+
)
|
|
13858
|
+
}))
|
|
13859
|
+
};
|
|
13860
|
+
return this;
|
|
13861
|
+
}
|
|
13722
13862
|
this.vesting = {
|
|
13723
13863
|
duration: Number(params.duration ?? DEFAULT_V3_VESTING_DURATION),
|
|
13724
13864
|
cliffDuration: params.cliffDuration ?? 0,
|
|
@@ -14051,6 +14191,19 @@ var DynamicAuctionBuilder = class _DynamicAuctionBuilder {
|
|
|
14051
14191
|
this.vesting = void 0;
|
|
14052
14192
|
return this;
|
|
14053
14193
|
}
|
|
14194
|
+
if (params.allocations) {
|
|
14195
|
+
this.vesting = {
|
|
14196
|
+
allocations: params.allocations.map((allocation, index) => ({
|
|
14197
|
+
recipient: allocation.recipient,
|
|
14198
|
+
amount: allocation.amount,
|
|
14199
|
+
schedule: normalizeBuilderVestingSchedule(
|
|
14200
|
+
allocation.schedule,
|
|
14201
|
+
`Vesting allocations[${index}].schedule`
|
|
14202
|
+
)
|
|
14203
|
+
}))
|
|
14204
|
+
};
|
|
14205
|
+
return this;
|
|
14206
|
+
}
|
|
14054
14207
|
this.vesting = {
|
|
14055
14208
|
duration: Number(params.duration ?? 0n),
|
|
14056
14209
|
cliffDuration: params.cliffDuration ?? 0,
|
|
@@ -14657,6 +14810,19 @@ var MulticurveBuilder = class _MulticurveBuilder {
|
|
|
14657
14810
|
this.vesting = void 0;
|
|
14658
14811
|
return this;
|
|
14659
14812
|
}
|
|
14813
|
+
if (params.allocations) {
|
|
14814
|
+
this.vesting = {
|
|
14815
|
+
allocations: params.allocations.map((allocation, index) => ({
|
|
14816
|
+
recipient: allocation.recipient,
|
|
14817
|
+
amount: allocation.amount,
|
|
14818
|
+
schedule: normalizeBuilderVestingSchedule(
|
|
14819
|
+
allocation.schedule,
|
|
14820
|
+
`Vesting allocations[${index}].schedule`
|
|
14821
|
+
)
|
|
14822
|
+
}))
|
|
14823
|
+
};
|
|
14824
|
+
return this;
|
|
14825
|
+
}
|
|
14660
14826
|
this.vesting = {
|
|
14661
14827
|
duration: Number(params.duration ?? 0n),
|
|
14662
14828
|
cliffDuration: params.cliffDuration ?? 0,
|
|
@@ -15046,6 +15212,19 @@ var OpeningAuctionBuilder = class _OpeningAuctionBuilder {
|
|
|
15046
15212
|
this.vesting = void 0;
|
|
15047
15213
|
return this;
|
|
15048
15214
|
}
|
|
15215
|
+
if (params.allocations) {
|
|
15216
|
+
this.vesting = {
|
|
15217
|
+
allocations: params.allocations.map((allocation, index) => ({
|
|
15218
|
+
recipient: allocation.recipient,
|
|
15219
|
+
amount: allocation.amount,
|
|
15220
|
+
schedule: normalizeBuilderVestingSchedule(
|
|
15221
|
+
allocation.schedule,
|
|
15222
|
+
`Vesting allocations[${index}].schedule`
|
|
15223
|
+
)
|
|
15224
|
+
}))
|
|
15225
|
+
};
|
|
15226
|
+
return this;
|
|
15227
|
+
}
|
|
15049
15228
|
this.vesting = {
|
|
15050
15229
|
duration: Number(params.duration ?? 0n),
|
|
15051
15230
|
cliffDuration: params.cliffDuration ?? 0,
|