Custom Fees
Arbitrary migration to Uniswap v4 pools with customizable fees
The V3 SDK includes support for creating Doppler V3 pools that can migrate their liquidity to Uniswap V4 with customizable fee streaming. This allows protocols to distribute trading fees to multiple beneficiaries over time, and importantly, customize the post-graduation fee amounts.
Overview
The "V4 migrator" enables:
Migration from v3 or v4 Doppler pools to Uniswap V4 pools
Fee streaming to multiple beneficiaries with custom shares
Time-locked liquidity with configurable duration
Support for both standard and no-op governance models
Usage with V3 SDK
Doppler v3 tokens can specify their usage of the Uniswap v4 migrator at the time of creation.
Integration with CreateV3PoolParams
The V3 SDK's CreateV3PoolParams
interface now includes an optional liquidityMigratorData
field:
interface CreateV3PoolParams {
// ... other parameters ...
liquidityMigratorData?: Hex; // Encoded V4 migration configuration
}
Step 1: Configure Beneficiaries
import { BeneficiaryData, WAD } from "doppler-v3-sdk";
// Shares must sum to exactly WAD (1e18)
const beneficiaries: BeneficiaryData[] = [
{
beneficiary: "0x...", // Treasury address
shares: WAD * 70n / 100n // 70% of fees
},
{
beneficiary: "0x...", // Development fund
shares: WAD * 20n / 100n // 20% of fees
},
{
beneficiary: "0x...", // Community rewards
shares: WAD * 10n / 100n // 10% of fees
}
];
Step 2: Sort and Validate Beneficiaries
Beneficiaries must be sorted by address in ascending order:
import { ReadWriteFactory } from "doppler-v3-sdk";
const factory = new ReadWriteFactory(airlockAddress, bundlerAddress);
const sortedBeneficiaries = factory.sortBeneficiaries(beneficiaries);
Step 3: Configure V4 Migrator
import { V4MigratorData } from "doppler-v3-sdk";
const migratorConfig: V4MigratorData = {
fee: 3000, // 0.3% fee tier
tickSpacing: 60, // Tick spacing for the V4 pool
lockDuration: 365 * 24 * 60 * 60, // 1 year lock
beneficiaries: sortedBeneficiaries
};
Step 4: Encode Configuration
const encodedMigratorData = factory.encodeV4MigratorData(migratorConfig);
Step 5: Create Pool with V4 Migration
const createParams = await factory.buildConfig({
integrator: "0x...",
userAddress: userAddress,
numeraire: addresses.unichain.weth,
contracts: {
tokenFactory: addresses.unichain.tokenFactory,
governanceFactory: addresses.unichain.governanceFactory,
poolInitializer: addresses.unichain.v3Initializer,
liquidityMigrator: addresses.unichain.liquidityMigrator, // V4 migrator
},
tokenConfig: {
name: "My Token",
symbol: "MTK",
tokenURI: "https://example.com/token-metadata.json"
},
// Pass the encoded migrator data
liquidityMigratorData: encodedMigratorData,
// ... other parameters
});
await factory.create(createParams);
Querying Migrator State
Use the ReadMigrator
class to query migrator configuration:
import { ReadMigrator } from "doppler-v3-sdk";
const migrator = new ReadMigrator(migratorAddress);
// Get V4 pool configuration
const poolKey = await migrator.getAssetData(token0, token1);
console.log(`Fee tier: ${poolKey.fee}`);
console.log(`Tick spacing: ${poolKey.tickSpacing}`);
// Get contract addresses
const locker = await migrator.locker(); // StreamableFeesLocker
const poolManager = await migrator.poolManager(); // V4 PoolManager
const airlock = await migrator.airlock(); // Airlock address
Important Considerations
Beneficiary Requirements
Sorted Order: Beneficiaries must be sorted by address (ascending)
Positive Shares: All shares must be greater than 0
Sum to WAD: Total shares must equal exactly 1e18 (100%)
Fee Tiers and Tick Spacing
Common V4 configurations:
0.01% fee → 1 tick spacing
0.05% fee → 10 tick spacing
0.3% fee → 60 tick spacing
1% fee → 200 tick spacing
Lock Duration
Minimum: No minimum (can be 0 for immediate unlocking)
Maximum: No maximum (can be set to centuries for permanent locks)
Typical: 1-4 years for protocol-owned liquidity
Helper Functions
sortBeneficiaries
const sorted = factory.sortBeneficiaries(beneficiaries);
Sorts beneficiaries by address in ascending order (required by contract).
encodeV4MigratorData
const encoded = factory.encodeV4MigratorData(migratorConfig);
Encodes the V4 migrator configuration for use in pool creation. Validates:
Beneficiaries are sorted
All shares are positive
Total shares equal WAD
Migration Flow
Pool Creation: Doppler V3 pool is created with initial liquidity
Trading Phase: Users trade in the V3 pool during the sale period
Migration Trigger: When conditions are met (time/volume), migration begins
Liquidity Exit: All liquidity is removed from the V3 pool
V4 Pool Creation: New V4 pool is created with full-range liquidity
Fee Streaming: Trading fees accumulate and stream to beneficiaries
Example: Multi-Beneficiary Setup
// Example: Protocol with multiple stakeholders
const beneficiaries: BeneficiaryData[] = [
{
beneficiary: "0x123...", // DAO Treasury
shares: WAD * 40n / 100n // 40%
},
{
beneficiary: "0x456...", // Core Team
shares: WAD * 25n / 100n // 25%
},
{
beneficiary: "0x789...", // Ecosystem Fund
shares: WAD * 20n / 100n // 20%
},
{
beneficiary: "0xabc...", // Bug Bounty Reserve
shares: WAD * 10n / 100n // 10%
},
{
beneficiary: "0xdef...", // Community Incentives
shares: WAD * 5n / 100n // 5%
}
];
// Create configuration with 2-year lock
const config: V4MigratorData = {
fee: 10000, // 1% fee for exotic pair
tickSpacing: 200, // Corresponding tick spacing
lockDuration: 2 * 365 * 24 * 60 * 60, // 2 years
beneficiaries: factory.sortBeneficiaries(beneficiaries)
};
See Also
Last updated