@anton-seriesfi/doppler-v3-sdk
Version:
SDK for interacting with Doppler v3 protocol
290 lines • 11.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReadWriteFactory = void 0;
const drift_1 = require("@delvtech/drift");
const ReadFactory_1 = require("./ReadFactory");
const viem_1 = require("viem");
// Constants for default configuration values
const ONE_YEAR_IN_SECONDS = 365 * 24 * 60 * 60;
const DEFAULT_START_TICK = 167000;
const DEFAULT_END_TICK = 200000;
const DEFAULT_NUM_POSITIONS = 10;
const DEFAULT_FEE = 10000; // 1% fee tier
const DEFAULT_VESTING_DURATION = BigInt(ONE_YEAR_IN_SECONDS);
const DEFAULT_INITIAL_SUPPLY_WAD = (0, viem_1.parseEther)("1000000000");
const DEFAULT_NUM_TOKENS_TO_SELL_WAD = (0, viem_1.parseEther)("900000000");
const DEFAULT_YEARLY_MINT_RATE_WAD = (0, viem_1.parseEther)("0.02");
const DEFAULT_PRE_MINT_WAD = (0, viem_1.parseEther)("9000000"); // 0.9% of the total supply
const DEFAULT_MAX_SHARE_TO_BE_SOLD = (0, viem_1.parseEther)("0.35");
const DEFAULT_INITIAL_VOTING_DELAY = 7200;
const DEFAULT_INITIAL_VOTING_PERIOD = 50400;
const DEFAULT_INITIAL_PROPOSAL_THRESHOLD = BigInt(0);
/**
* Factory class for creating and managing Doppler V3 pools with read/write capabilities
*/
class ReadWriteFactory extends ReadFactory_1.ReadFactory {
/**
* Create a new ReadWriteFactory instance
* @param address Contract address
* @param drift Drift instance for blockchain interaction
* @param defaultConfigs Optional default configurations
*/
constructor(address, drift = (0, drift_1.createDrift)(), defaultConfigs) {
super(address, drift);
/**
* Generate a random salt using cryptographic random values
* @param account User address to incorporate into salt
* @returns Hex string of generated salt
*/
this.generateRandomSalt = (account) => {
const array = new Uint8Array(32);
// Cross-platform random generation
if (typeof window !== "undefined" && window.crypto) {
window.crypto.getRandomValues(array);
}
else {
array.set(require("crypto").randomBytes(32));
}
// Incorporate user address into salt
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("")}`;
};
// 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 (0, viem_1.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 (0, viem_1.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 (0, viem_1.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;
while (isToken0) {
const encoded = this.encode(params);
createParams = encoded.createParams;
const simulateResult = await this.simulateCreate(createParams);
asset = simulateResult.asset;
isToken0 = Number(asset) < Number(params.numeraire);
}
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 });
}
/**
* 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);
}
}
exports.ReadWriteFactory = ReadWriteFactory;
//# sourceMappingURL=ReadWriteFactory.js.map