UNPKG

doppler-v3-sdk

Version:
305 lines 12.3 kB
import { createDrift, } from "@delvtech/drift"; import { ReadFactory } from "./ReadFactory"; import { BundlerAbi } from "../../abis"; import { encodeAbiParameters, parseEther, toHex } from "viem"; // Constants for default configuration values export const ONE_YEAR_IN_SECONDS = 365 * 24 * 60 * 60; export const DEFAULT_START_TICK = 175000; export const DEFAULT_END_TICK = 225000; export const DEFAULT_NUM_POSITIONS = 15; export const DEFAULT_FEE = 10000; // 1% fee tier export const DEFAULT_VESTING_DURATION = BigInt(ONE_YEAR_IN_SECONDS); export const DEFAULT_INITIAL_SUPPLY_WAD = parseEther("1000000000"); export const DEFAULT_NUM_TOKENS_TO_SELL_WAD = parseEther("900000000"); export const DEFAULT_YEARLY_MINT_RATE_WAD = parseEther("0.02"); export const DEFAULT_PRE_MINT_WAD = parseEther("9000000"); // 0.9% of the total supply export const DEFAULT_MAX_SHARE_TO_BE_SOLD = parseEther("0.35"); export const DEFAULT_INITIAL_VOTING_DELAY = 172800; export const DEFAULT_INITIAL_VOTING_PERIOD = 1209600; export const DEFAULT_INITIAL_PROPOSAL_THRESHOLD = BigInt(0); /** * Factory class for creating and managing Doppler V3 pools with read/write capabilities */ export class ReadWriteFactory extends ReadFactory { /** * Create a new ReadWriteFactory instance * @param address Contract address * @param drift Drift instance for blockchain interaction * @param defaultConfigs Optional default configurations */ constructor(address, bundlerAddress, drift = createDrift(), defaultConfigs) { super(address, drift); /** * Generate a random salt * @param account User address to incorporate into salt * @returns Hex string of generated salt */ this.generateRandomSalt = (account) => { const array = new Uint8Array(32); // Sequential byte generation for (let i = 0; i < 32; i++) { array[i] = i; } if (account) { const addressBytes = account.slice(2).padStart(40, "0"); for (let i = 0; i < 20; i++) { const addressByte = parseInt(addressBytes.slice(i * 2, (i + 1) * 2), 16); array[i] ^= addressByte; } } return `0x${Array.from(array) .map((b) => b.toString(16).padStart(2, "0")) .join("")}`; }; this.bundler = drift.contract({ abi: BundlerAbi, address: bundlerAddress, }); // Initialize default configurations with fallback values this.defaultV3PoolConfig = defaultConfigs?.defaultV3PoolConfig ?? { startTick: DEFAULT_START_TICK, endTick: DEFAULT_END_TICK, numPositions: DEFAULT_NUM_POSITIONS, maxShareToBeSold: DEFAULT_MAX_SHARE_TO_BE_SOLD, fee: DEFAULT_FEE, }; this.defaultVestingConfig = defaultConfigs?.defaultVestingConfig ?? { yearlyMintRate: DEFAULT_YEARLY_MINT_RATE_WAD, vestingDuration: DEFAULT_VESTING_DURATION, recipients: [], amounts: [], }; this.defaultSaleConfig = defaultConfigs?.defaultSaleConfig ?? { initialSupply: DEFAULT_INITIAL_SUPPLY_WAD, numTokensToSell: DEFAULT_NUM_TOKENS_TO_SELL_WAD, }; this.defaultGovernanceConfig = defaultConfigs?.defaultGovernanceConfig ?? { initialVotingDelay: DEFAULT_INITIAL_VOTING_DELAY, initialVotingPeriod: DEFAULT_INITIAL_VOTING_PERIOD, initialProposalThreshold: DEFAULT_INITIAL_PROPOSAL_THRESHOLD, }; } /** * Merge user configuration with defaults * @param config User-provided partial configuration * @param defaults Full default configuration * @returns Merged configuration object */ mergeWithDefaults(config, defaults) { return { ...defaults, ...config }; } /** * Get merged sale configuration * @param saleConfig Optional partial sale config * @returns Complete SaleConfig */ getMergedSaleConfig(saleConfig) { return this.mergeWithDefaults(saleConfig, this.defaultSaleConfig); } /** * Get merged pool configuration * @param v3PoolConfig Optional partial pool config * @returns Complete V3PoolConfig */ getMergedV3PoolConfig(v3PoolConfig) { return this.mergeWithDefaults(v3PoolConfig, this.defaultV3PoolConfig); } /** * Get merged governance configuration * @param governanceConfig Optional partial governance config * @returns Complete GovernanceConfig */ getMergedGovernanceConfig(governanceConfig) { return this.mergeWithDefaults(governanceConfig, this.defaultGovernanceConfig); } /** * Get merged vesting configuration * @param config Vesting config or "default" preset * @param userAddress User address for default recipient * @returns Complete VestingConfig */ getMergedVestingConfig(config, userAddress) { const base = config === "default" ? this.defaultVestingConfig : config; return { ...base, recipients: config === "default" ? [userAddress] : [...base.recipients], amounts: config === "default" ? [DEFAULT_PRE_MINT_WAD] : [...base.amounts], }; } /** * Encode pool initialization data for contract calls * @param v3PoolConfig Complete pool configuration * @returns ABI-encoded initialization data */ encodePoolInitializerData(v3PoolConfig) { return encodeAbiParameters([ { type: "uint24" }, { type: "int24" }, { type: "int24" }, { type: "uint16" }, { type: "uint256" }, ], [ v3PoolConfig.fee, v3PoolConfig.startTick, v3PoolConfig.endTick, v3PoolConfig.numPositions, v3PoolConfig.maxShareToBeSold, ]); } /** * Encode token factory initialization data * @param tokenConfig Token metadata * @param vestingConfig Vesting schedule * @returns ABI-encoded token factory data */ encodeTokenFactoryData(tokenConfig, vestingConfig) { return encodeAbiParameters([ { type: "string" }, { type: "string" }, { type: "uint256" }, { type: "uint256" }, { type: "address[]" }, { type: "uint256[]" }, { type: "string" }, ], [ tokenConfig.name, tokenConfig.symbol, vestingConfig.yearlyMintRate, vestingConfig.vestingDuration, vestingConfig.recipients, vestingConfig.amounts, tokenConfig.tokenURI, ]); } /** * Encode governance factory initialization data * @param tokenConfig Token metadata * @returns ABI-encoded governance data */ encodeGovernanceFactoryData(tokenConfig, governanceConfig) { return encodeAbiParameters([ { type: "string" }, { type: "uint48" }, { type: "uint32" }, { type: "uint256" }, ], [ tokenConfig.name, Number(governanceConfig.initialVotingDelay), Number(governanceConfig.initialVotingPeriod), governanceConfig.initialProposalThreshold, ]); } /** * Encode all parameters for pool creation * @param params CreateV3PoolParams input parameters * @returns Object containing create parameters and final pool config * @throws Error if user address is missing or invalid tick range */ encode(params) { const { userAddress, numeraire, integrator, contracts, tokenConfig } = params; if (!userAddress) { throw new Error("User address is required. Is a wallet connected?"); } // Merge configurations with defaults const vestingConfig = this.getMergedVestingConfig(params.vestingConfig, userAddress); const v3PoolConfig = this.getMergedV3PoolConfig(params.v3PoolConfig); const saleConfig = this.getMergedSaleConfig(params.saleConfig); const governanceConfig = this.getMergedGovernanceConfig(params.governanceConfig); // Validate tick configuration if (v3PoolConfig.startTick > v3PoolConfig.endTick) { throw new Error("Invalid start and end ticks. Start tick must be less than end tick."); } // Generate unique salt and encode contract data const salt = this.generateRandomSalt(userAddress); const governanceFactoryData = this.encodeGovernanceFactoryData(tokenConfig, governanceConfig); const tokenFactoryData = this.encodeTokenFactoryData(tokenConfig, vestingConfig); const poolInitializerData = this.encodePoolInitializerData(v3PoolConfig); const liquidityMigratorData = "0x"; // Prepare final arguments const { tokenFactory, governanceFactory, v3Initializer: poolInitializer, liquidityMigrator, } = contracts; const { initialSupply, numTokensToSell } = saleConfig; const args = { initialSupply, numTokensToSell, numeraire, tokenFactory, tokenFactoryData, governanceFactory, governanceFactoryData, poolInitializer, poolInitializerData, liquidityMigrator, liquidityMigratorData, integrator, salt, }; return { createParams: args, v3PoolConfig, }; } /** * Encode creation data with token order validation * @param params CreateV3PoolParams input parameters * @returns Finalized create parameters with adjusted ticks if needed */ async encodeCreateData(params) { let isToken0 = true; let createParams; let asset; let i = 0n; while (isToken0) { const encoded = this.encode(params); createParams = encoded.createParams; createParams.salt = this.generateRandomSalt(toHex(BigInt(params.userAddress) + BigInt(i))); const simulateResult = await this.simulateCreate(createParams); asset = simulateResult.asset; isToken0 = Number(asset) < Number(params.numeraire); i++; } return createParams; } /** * Execute pool creation transaction * @param params Finalized create parameters * @param options Write options and mined handlers * @returns Transaction hash */ async create(params, options) { return this.airlock.write("create", { createData: params }, options); } /** * Simulate pool creation transaction * @param params Create parameters * @returns Simulation results */ async simulateCreate(params) { return this.airlock.simulateWrite("create", { createData: params }); } async simulateBundleExactOutput(createData, params) { return this.bundler.simulateWrite("simulateBundleExactOut", { createData, params: { ...params }, }); } async simulateBundleExactInput(createData, params) { return this.bundler.simulateWrite("simulateBundleExactIn", { createData, params: { ...params }, }); } async bundle(createData, commands, inputs, options) { return this.bundler.write("bundle", { createData, commands, inputs }, options); } /** * Update default configurations * @param configs Partial configuration overrides */ updateDefaultConfigs(configs) { this.defaultV3PoolConfig = this.mergeWithDefaults(configs.defaultV3PoolConfig || {}, this.defaultV3PoolConfig); this.defaultVestingConfig = this.mergeWithDefaults(configs.defaultVestingConfig || {}, this.defaultVestingConfig); this.defaultSaleConfig = this.mergeWithDefaults(configs.defaultSaleConfig || {}, this.defaultSaleConfig); this.defaultGovernanceConfig = this.mergeWithDefaults(configs.defaultGovernanceConfig || {}, this.defaultGovernanceConfig); } } //# sourceMappingURL=ReadWriteFactory.js.map