Overview
When a DBC virtual pool’s quote_reserve reaches migration_quote_threshold, the bonding curve phase ends and the pool migrates to a real AMM. This is automatic and permissionless — any caller can trigger the migration instruction once the threshold is hit.
Migration Trigger
if virtual_pool.quote_reserve >= config.migration_quote_threshold → migrate
The finish_curve_timestamp is recorded on the VirtualPool when the threshold is first crossed. Migration can be called at any point after this.
Trading is paused once the curve is complete (PoolIsCompleted error). Migration must happen before normal trading resumes.
Migration Options
migration_option | Target Pool |
|---|
MeteoraDamm (0) | DAMM v1 (constant-product, supports stable pools) |
DammV2 (1) | DAMM v2 (concentrated liquidity, custom fee modes) |
Migration Steps (On-Chain)
1. PreBonding → LockedVesting (standard flow)
migrateMeteoraDammLockLpToken (for DAMM v1)
migrateDammV2CreatePool + lockLp (for DAMM v2)
For most launches (no Jupiter lock):
migration_progress = PreBondingCurve
- Threshold hit → caller invokes migrate instruction
- Program creates DAMM pool with
migration_sqrt_price and seeds liquidity
- LP positions are distributed to partner, creator, and protocol
- Vesting schedules are initialized on the LP positions
migration_progress = LockedVesting → CreatedPool
2. With Jupiter Lock (PostBonding flow)
Some launches integrate with Jupiter’s lock mechanism:
PreBondingCurve → threshold hit
PostBondingCurve (Jupiter lock processing)
LockedVesting → CreatedPool
Liquidity Distribution at Migration
The total migrated liquidity is split:
Total LP
├── Partner LP (partner_liquidity_percentage %)
│ ├── Permanently locked (partner_permanent_locked_liquidity_percentage %)
│ └── Vesting (partner_liquidity_vesting_info schedule)
├── Creator LP (creator_liquidity_percentage %)
│ ├── Permanently locked (creator_permanent_locked_liquidity_percentage %)
│ └── Vesting (creator_liquidity_vesting_info schedule)
└── Protocol LP (remainder)
Vesting schedules use the same cliff + periodic model as DAMM v2 inner vesting:
| Parameter | Description |
|---|
cliff_duration_from_migration_time | Seconds after migration before first unlock |
frequency | Seconds between each periodic release |
number_of_period | Total number of release periods |
cliff_unlock_amount | Amount released at the cliff |
amount_per_period | Amount released per period after the cliff |
Migration Fee
A one-time migration fee is taken from the migrated quote reserves:
migration_fee_amount = total_quote × migration_fee_bps / 10_000
Split between protocol and creator:
migration_fee_percentage % → protocol
creator_migration_fee_percentage % → creator
Both parties can claim their migration fee separately via withdrawMigrationFee.
The migration_fee_withdraw_status bitmask tracks withdrawal status:
- bit 1 (
0b010) = creator has withdrawn
- bit 2 (
0b100) = partner has withdrawn
Surplus Claims
After migration, there may be surplus quote tokens beyond what’s needed for the DAMM pool. These are claimable:
| Claimant | Instruction | Condition |
|---|
| Protocol | withdrawProtocolSurplus | is_protocol_withdraw_surplus = 0 |
| Partner | withdrawPartnerSurplus | is_partner_withdraw_surplus = 0 |
| Creator | withdrawCreatorSurplus | is_creator_withdraw_surplus = 0 |
The surplus split is determined by PARTNER_AND_CREATOR_SURPLUS_SHARE constant.
Post-Migration: DAMM v2 Pool Parameters
When migration_option = DammV2, the created DAMM pool uses:
| DBC Config Field | Maps to DAMM v2 |
|---|
migration_sqrt_price | initSqrtPrice |
migrated_collect_fee_mode | collectFeeMode |
migrated_pool_fee_bps | Base fee |
migrated_pool_base_fee_mode | BaseFeeMode |
migrated_dynamic_fee | Dynamic fee enabled |
Fixed Supply Migration
For fixed-supply tokens, leftover base tokens (unsold at migration) are returned to leftover_receiver:
leftover = pre_migration_token_supply - swap_base_amount - post_migration_token_supply
The is_withdraw_leftover flag on VirtualPool tracks whether this has happened.