TypeScript REST API for creating Doppler launches.
This project is in active development & not ready for production use.
POST /v1/launchesPOST /v1/launches/multicurve(alias)POST /v1/launches/static(alias)POST /v1/launches/dynamic(alias)GET /v1/launches/:launchIdGET /v1/capabilitiesGET /healthGET /ready
npm install
cp .env.example .env
npm run dev- Auction types:
multicurve(recommended default on V4-capable networks)dynamic(for higher-value assets that need maximally capital-efficient price discovery; supportsmigration.type="uniswapV2"ormigration.type="uniswapV4"in this API profile)static(Uniswap V3 static launch with lockable beneficiaries; compatibility fallback for networks without Uniswap V4 support)
- Multicurve initializer modes:
standard(implemented via scheduled initializer withstartTime=0)scheduled(startTimerequired)decay(startFee,durationSeconds, optionalstartTime)rehype(hook-based initializer config)
- Migration modes:
noOpfor multicurve/staticuniswapV2anduniswapV4for dynamicuniswapV3is not supported and returns501 MIGRATION_NOT_IMPLEMENTED
- Governance:
enabled=falseis the active profile, eg.noOp - Token allocation profile:
- Default: 100% of
totalSupplyis allocated to the multicurve market. - Optional: set
economics.tokensForSaleto allocate less to the market. - Remainder (
totalSupply - tokensForSale) is allocated to non-market allocation recipients. - Optional: set
economics.allocations.recipients(max 10 unique recipients) to split the non-market remainder.
- Default: 100% of
- Multicurve design reference: Doppler Multicurve whitepaper.
- Guidance: prefer
multicurvewhenever the target chain has Uniswap V4 support. Usestaticonly when V4 is unavailable.
- This API profile is at feature parity with the other Doppler launch APIs for the supported launch flows.
<chainId>:<txHash>
Example:
84532:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
POST /v1/launches
x-api-key: <API_KEY>
content-type: application/json{
"chainId": 84532,
"userAddress": "0x1111111111111111111111111111111111111111",
"integrationAddress": "0x1111111111111111111111111111111111111111",
"tokenMetadata": {
"name": "My Token",
"symbol": "MTK",
"tokenURI": "ipfs://my-token-metadata"
},
"economics": {
"totalSupply": "1000000000000000000000000",
"tokensForSale": "800000000000000000000000",
"allocations": {
"recipients": [
{
"address": "0x1111111111111111111111111111111111111111",
"amount": "100000000000000000000000"
},
{
"address": "0x2222222222222222222222222222222222222222",
"amount": "100000000000000000000000"
}
],
"mode": "vest",
"durationSeconds": 7776000
}
},
"pricing": {
"numerairePriceUsd": 3000
},
"governance": {
"enabled": false,
"mode": "noOp"
},
"migration": {
"type": "noOp"
},
"auction": {
"type": "multicurve",
"curveConfig": {
"type": "preset",
"presets": ["low", "medium", "high"]
},
"initializer": {
"type": "standard"
}
}
}Use this when you want deterministic, non-default market-cap bands instead of presets.
{
"auction": {
"type": "multicurve",
"curveConfig": {
"type": "ranges",
"fee": 15000,
"tickSpacing": 300,
"curves": [
{
"marketCapStartUsd": 100,
"marketCapEndUsd": 10000,
"numPositions": 11,
"sharesWad": "200000000000000000"
},
{
"marketCapStartUsd": 10000,
"marketCapEndUsd": 100000,
"numPositions": 11,
"sharesWad": "300000000000000000"
},
{
"marketCapStartUsd": 100000,
"marketCapEndUsd": "max",
"numPositions": 11,
"sharesWad": "500000000000000000"
}
]
}
}
}Use this only for the static fallback path.
{
"auction": {
"type": "static",
"curveConfig": {
"type": "range",
"marketCapStartUsd": 100,
"marketCapEndUsd": 100000
}
}
}Use this for the V4 dynamic flow. Dynamic exits/migrates when maxProceeds is reached, or at auction end when minProceeds is satisfied.
Dynamic is intended for assets with well-known value that benefit from maximally capital-efficient price discovery.
{
"migration": {
"type": "uniswapV2"
},
"auction": {
"type": "dynamic",
"curveConfig": {
"type": "range",
"marketCapStartUsd": 100,
"marketCapMinUsd": 50,
"minProceeds": "0.01",
"maxProceeds": "0.1",
"durationSeconds": 86400
}
}
}{
"launchId": "84532:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"chainId": 84532,
"txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"statusUrl": "/v1/launches/84532:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"predicted": {
"tokenAddress": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"poolId": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"gasEstimate": "12500000"
},
"effectiveConfig": {
"tokensForSale": "800000000000000000000000",
"allocationAmount": "200000000000000000000000",
"allocationRecipient": "0x1111111111111111111111111111111111111111",
"allocationRecipients": [
{
"address": "0x1111111111111111111111111111111111111111",
"amount": "100000000000000000000000"
},
{
"address": "0x2222222222222222222222222222222222222222",
"amount": "100000000000000000000000"
}
],
"allocationLockMode": "vest",
"allocationLockDurationSeconds": 7776000,
"numeraireAddress": "0x4200000000000000000000000000000000000006",
"numerairePriceUsd": 3000,
"feeBeneficiariesSource": "default"
}
}{
"launchId": "84532:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"chainId": 84532,
"txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "pending",
"confirmations": 0
}{
"launchId": "84532:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"chainId": 84532,
"txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "confirmed",
"confirmations": 2,
"result": {
"tokenAddress": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"poolOrHookAddress": "0xdddddddddddddddddddddddddddddddddddddddd",
"poolId": "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"blockNumber": "12345678"
}
}{
"launchId": "84532:0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"chainId": 84532,
"txHash": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"status": "reverted",
"confirmations": 1,
"error": {
"code": "TX_REVERTED",
"message": "Transaction reverted on-chain"
}
}{
"defaultChainId": 84532,
"pricing": {
"enabled": true,
"provider": "coingecko"
},
"chains": [
{
"chainId": 84532,
"auctionTypes": ["multicurve", "dynamic"],
"multicurveInitializers": ["standard", "scheduled", "decay", "rehype"],
"migrationModes": ["noOp", "uniswapV2"],
"governanceModes": ["noOp", "default"],
"governanceEnabled": true
}
]
}GET /health: process livenessGET /ready: dependency readiness (chain RPC checks)
Example GET /health:
{ "status": "ok" }economics.tokensForSaleis optional:- if omitted,
tokensForSale = totalSupply(100% sold to market). - if provided, it must be
> 0and<= totalSupply. - if
tokensForSale < totalSupply, it must be at least20%oftotalSupply.
- if omitted,
- Non-market allocation is automatic:
allocationAmount = totalSupply - tokensForSale.- default recipient is
userAddress. - default lock mode is
vestfor90days when non-market allocation exists. - lock modes:
vest,unlock,vault.
- Optional explicit split across recipients:
economics.allocations.recipientssupports up to10unique allocation recipients.- no duplicate addresses are allowed.
- allocation amounts must sum exactly to
totalSupply - tokensForSale. - if allocations are provided and
tokensForSaleis omitted, API derivestokensForSale.
- Multicurve initializer:
- default is
standard(implemented as scheduled withstartTime=0). scheduledrequiresauction.initializer.startTime.decayrequiresstartFeeanddurationSeconds(optionalstartTime).rehyperequires hook config and percent wad fields that sum to1e18.
- default is
- Multicurve curve selection:
- presets are convenient defaults.
- explicit
rangesare recommended when you need intentional market-cap bands instead of default tiers. - custom multicurve swap fees are supported via
curveConfig.fee(custom values supported; tick spacing can be derived or provided).
- Static launch curve config:
auction.type="static"requiresauction.curveConfig.curveConfig.type="preset"supportspreset: "low" | "medium" | "high".curveConfig.type="range"supports explicitmarketCapStartUsdandmarketCapEndUsd.- custom static fee input is supported via
curveConfig.fee, but Uniswap V3 still enforces valid V3 fee tiers onchain. - static launches always use lockable beneficiaries (request values or default 95% user / 5% protocol owner).
- use static only as a fallback when the target chain does not support Uniswap V4/multicurve.
- Dynamic launch curve config:
auction.type="dynamic"requiresauction.curveConfig.curveConfig.type="range"requires:marketCapStartUsdmarketCapMinUsdminProceeds(decimal string in numeraire units)maxProceeds(decimal string in numeraire units)
- optional:
durationSeconds,epochLengthSeconds,fee,tickSpacing,gamma,numPdSlugs - custom dynamic fees are supported via
curveConfig.fee. - dynamic launches require
migration.type="uniswapV2"ormigration.type="uniswapV4"in this API profile. - for
migration.type="uniswapV4", requestmigration.feeandmigration.tickSpacing. - for
migration.type="uniswapV4", streamable fee beneficiaries are derived fromfeeBeneficiaries(or the default 95/5 split). migration.type="uniswapV3"is reserved and currently returns501 MIGRATION_NOT_IMPLEMENTED.
- Percentage-based allocation is supported by converting percent to amount:
tokensForSale = totalSupply * salePercent / 100- Example: 20% sale means 80% non-market allocation.
integrationAddressis optional.governanceis binary at create time:- omitted/
false=> no governance trueor{ "enabled": true }=> default token-holder governance (OpenZeppelin Governor via protocol governance factory)
- omitted/
pricing.numerairePriceUsdoverrides provider pricing.- If auto-pricing is unavailable, caller must pass
pricing.numerairePriceUsd. - If
feeBeneficiariesis omitted, API applies default split:userAddress: 95%- protocol owner: 5%
feeBeneficiariesrequest constraints:- supports up to
10unique beneficiary addresses. - shares use WAD precision and must sum to
1e18(100%) when protocol owner is included. - if protocol owner is omitted, provided shares must sum to
95%(0.95e18) and API appends protocol owner at5%. - if protocol owner is provided, it must have at least
5%(WAD / 20).
- supports up to
See docs/mvp-launch.md for a concise MVP launch example and a full defaults-resolution table.
npm test
npm run test:static
npm run test:dynamic
npm run test:live
npm run test:live:static
npm run test:live:dynamic
npm run test:live:v2migration
npm run test:live:v4migration
npm run test:live:multicurve
npm run test:live:multicurve:defaults
npm run test:live:fees
npm run test:live:governance
npm run test:live --verbosetest:live performs real on-chain creation and verification when LIVE_TEST_ENABLE=true and funded credentials are configured.
By default, live output is concise (launch summary table). Use --verbose for full per-launch parameter and verification tables.
Live launch tests run sequentially to avoid nonce conflicts from a single funded signer.