update create pool from prices script:

This commit is contained in:
2025-11-24 12:04:07 -04:00
parent 5b450ab303
commit ea44e0d941

View File

@@ -7,7 +7,6 @@
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { readFile } from 'fs/promises'; import { readFile } from 'fs/promises';
import { config } from 'dotenv'; import { config } from 'dotenv';
import IPartyPlannerABI from "../src/contracts/IPartyPlannerABI.ts";
// Load environment variables from .env-secret // Load environment variables from .env-secret
config({ path: new URL('../.env-secret', import.meta.url).pathname }); config({ path: new URL('../.env-secret', import.meta.url).pathname });
@@ -16,85 +15,133 @@ config({ path: new URL('../.env-secret', import.meta.url).pathname });
// CONFIGURATION // CONFIGURATION
// ============================================================================ // ============================================================================
const RPC_URL = process.env.MAINNET_RPC_URL; // Network flag: 'mockchain' or 'mainnet'
const PRIVATE_KEY = process.env.PRIVATE_KEY; const NETWORK = process.env.NETWORK || 'mainnet';
const RECEIVER_ADDRESS = process.env.RECEIVER_ADDRESS;
if (!RPC_URL || !PRIVATE_KEY || !RECEIVER_ADDRESS) { // Network-specific configuration
console.error('[!] Missing required environment variables in .env-secret file'); const NETWORK_CONFIG = {
console.error(' Required: MAINNET_RPC_URL, PRIVATE_KEY, RECEIVER_ADDRESS'); mockchain: {
process.exit(1); rpcUrl: 'http://localhost:8545',
} privateKey: '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a', // Dev wallet #4 (sender)
receiverPrivateKey: '0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356', // Receiver wallet
// Curated token list with real addresses (Ethereum Mainnet) receiverAddress: '0x14dC79964da2C08b23698B3D3cc7Ca32193d9955', // Receiver public key
const TEST_TOKENS = { chainId: '31337',
USDT: { tokens: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // Mainnet USDT USDT: {
coingeckoId: 'tether', address: '0xbdEd0D2bf404bdcBa897a74E6657f1f12e5C6fb6', // USXD on mockchain
decimals: 6, coingeckoId: 'tether',
feePpm: 400 // 0.00004 = 0.004% = 40 ppm decimals: 6,
feePpm: 400
},
USDC: {
address: '0x2910E325cf29dd912E3476B61ef12F49cb931096', // FUSD on mockchain
coingeckoId: 'usd-coin',
decimals: 6,
feePpm: 400
}
}
}, },
USDC: { mainnet: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // Mainnet USDC rpcUrl: process.env.MAINNET_RPC_URL,
coingeckoId: 'usd-coin', privateKey: process.env.PRIVATE_KEY,
decimals: 6, receiverAddress: '0xd3b310bd32d782f89eea49cb79656bcaccde7213', // Same as payer for mainnet
feePpm: 400 // 0.00004 = 0.004% = 40 ppm chainId: '1',
}, tokens: {
WBTC: { USDT: {
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', // Mainnet WBTC address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
coingeckoId: 'wrapped-bitcoin', coingeckoId: 'tether',
decimals: 8, decimals: 6,
feePpm: 3000 // 0.00030 = 0.03% = 300 ppm feePpm: 40 // 0.0004%
}, },
WETH: { USDC: {
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // Mainnet WETH address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
coingeckoId: 'weth', coingeckoId: 'usd-coin',
decimals: 18, decimals: 6,
feePpm: 3500 // 0.00035 = 0.035% = 350 ppm feePpm: 40 // 0.0004%
}, },
BNB: { WBTC: {
address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', // Mainnet BNB (Binance-Peg) address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
coingeckoId: 'binancecoin', coingeckoId: 'wrapped-bitcoin',
decimals: 18, decimals: 8,
feePpm: 4500 // 0.00045 = 0.045% = 450 ppm feePpm: 300 // 0.00030%
}, },
WSOL: { WETH: {
address: '0xD31a59c85aE9D8edEFeC411D448f90841571b89c', // Mainnet Wormhole Wrapped SOL address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
coingeckoId: 'solana', coingeckoId: 'weth',
decimals: 9, decimals: 18,
feePpm: 9500 // 0.00095 = 0.095% = 950 ppm feePpm: 350 // 0.0035%
}, },
TRX: { UNI: {
address: '0x50327c6c5a14DCaDE707ABad2E27eB517df87AB5', // Mainnet TRX (Wrapped) address: '0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984',
coingeckoId: 'tron', coingeckoId: 'uniswap',
decimals: 6, decimals: 18,
feePpm: 9500 // 0.00095 = 0.095% = 950 ppm feePpm: 1450 // 0.00145%
}, },
AAVE: { WSOL: {
address: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', // Mainnet AAVE address: '0xD31a59c85aE9D8edEFeC411D448f90841571b89c', // Wormhole Wrapped SOL
coingeckoId: 'aave', coingeckoId: 'solana',
decimals: 18, decimals: 9,
feePpm: 14500 // 0.00145 = 0.145% = 1450 ppm feePpm: 950 // 0.00095%
}, },
PEPE: { TRX: {
address: '0x6982508145454Ce325dDbE47a25d4ec3d2311933', // Mainnet PEPE address: '0x50327c6c5a14DCaDE707ABad2E27eB517df87AB5',
coingeckoId: 'pepe', coingeckoId: 'tron',
decimals: 18, decimals: 6,
feePpm: 21500 // 0.00215 = 0.215% = 2150 ppm feePpm: 950 // 0.00095%
}, },
SHIB: { AAVE: {
address: '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE', // Mainnet SHIB address: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9',
coingeckoId: 'shiba-inu', coingeckoId: 'aave',
decimals: 18, decimals: 18,
feePpm: 21500 // 0.00215 = 0.215% = 2150 ppm feePpm: 1450 // 0.00145%
},
PEPE: {
address: '0x6982508145454Ce325dDbE47a25d4ec3d2311933',
coingeckoId: 'pepe',
decimals: 18,
feePpm: 2150 // 0.00215%
},
SHIB: {
address: '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE',
coingeckoId: 'shiba-inu',
decimals: 18,
feePpm: 2150 // 0.00215%
}
}
} }
}; };
// Get current network config
const currentConfig = NETWORK_CONFIG[NETWORK];
if (!currentConfig) {
console.error(`[!] Invalid NETWORK: ${NETWORK}. Must be 'mockchain' or 'mainnet'`);
process.exit(1);
}
const RPC_URL = currentConfig.rpcUrl;
const PRIVATE_KEY = currentConfig.privateKey;
const RECEIVER_ADDRESS = currentConfig.receiverAddress || process.env.RECEIVER_ADDRESS;
// Validate required config for mainnet
if (NETWORK === 'mainnet' && (!RPC_URL || !PRIVATE_KEY)) {
console.error('[!] Missing required environment variables for mainnet');
console.error(' Required: MAINNET_RPC_URL, PRIVATE_KEY');
process.exit(1);
}
if (!RECEIVER_ADDRESS) {
console.error('[!] Missing RECEIVER_ADDRESS in .env-secret file');
process.exit(1);
}
// Use network-specific tokens
const TEST_TOKENS = currentConfig.tokens;
// Default pool parameters // Default pool parameters
const DEFAULT_POOL_PARAMS = { const DEFAULT_POOL_PARAMS = {
name: 'Staging LP Pool', name: 'Liquidity Party POC',
symbol: 'SLP', symbol: 'POC.LP',
kappa: ethers.BigNumber.from('10000000000000000'), // 0.01 * 1e18 kappa: ethers.BigNumber.from('184467440737095520'), //0.01 * 2^64
swapFeesPpm: Object.values(TEST_TOKENS).map(t => t.feePpm), swapFeesPpm: Object.values(TEST_TOKENS).map(t => t.feePpm),
flashFeePpm: 5, // 0.0005% flashFeePpm: 5, // 0.0005%
stable: false, stable: false,
@@ -110,7 +157,7 @@ const INPUT_USD_AMOUNT = 1;
// ============================================================================ // ============================================================================
const chainInfoData = JSON.parse(await readFile(new URL('../src/contracts/liqp-deployments.json', import.meta.url), 'utf-8')); const chainInfoData = JSON.parse(await readFile(new URL('../src/contracts/liqp-deployments.json', import.meta.url), 'utf-8'));
const PARTY_PLANNER_ADDRESS = chainInfoData['1'].v1.PartyPlanner; const PARTY_PLANNER_ADDRESS = chainInfoData[currentConfig.chainId].v1.PartyPlanner;
const ERC20ABI = [ const ERC20ABI = [
{ "type": "function", "name": "balanceOf", "stateMutability": "view", "inputs": [{ "name": "account", "type": "address" }], "outputs": [{ "name": "", "type": "uint256" }] }, { "type": "function", "name": "balanceOf", "stateMutability": "view", "inputs": [{ "name": "account", "type": "address" }], "outputs": [{ "name": "", "type": "uint256" }] },
@@ -189,15 +236,15 @@ function calculateTokenAmounts(prices, usdAmount) {
/** /**
* Check token balances * Check token balances
*/ */
async function checkBalances(provider, wallet, tokenAmounts) { async function checkBalances(provider, wallet, tokenAmounts, receiverAddress) {
console.log(`\n[~] Checking token balances for wallet: ${wallet.address}`); console.log(`\n[~] Checking token balances for receiver wallet: ${receiverAddress}`);
const balances = {}; const balances = {};
let hasEnoughBalance = true; let hasEnoughBalance = true;
for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) { for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) {
const tokenContract = new ethers.Contract(tokenInfo.address, ERC20ABI, provider); const tokenContract = new ethers.Contract(tokenInfo.address, ERC20ABI, provider);
const balance = await tokenContract.balanceOf(wallet.address); const balance = await tokenContract.balanceOf(receiverAddress);
const requiredAmount = tokenAmounts[symbol]; const requiredAmount = tokenAmounts[symbol];
balances[symbol] = balance; balances[symbol] = balance;
@@ -214,7 +261,7 @@ async function checkBalances(provider, wallet, tokenAmounts) {
} }
if (!hasEnoughBalance) { if (!hasEnoughBalance) {
console.log(`\n[!] Insufficient token balance. Please ensure your wallet has enough tokens.`); console.log(`\n[!] Insufficient token balance. Please ensure receiver wallet has enough tokens.`);
throw new Error('Insufficient token balance'); throw new Error('Insufficient token balance');
} }
@@ -225,26 +272,30 @@ async function checkBalances(provider, wallet, tokenAmounts) {
/** /**
* Approve tokens for the PartyPlanner contract * Approve tokens for the PartyPlanner contract
*/ */
async function approveTokens(wallet, tokenAmounts) { async function approveTokens(provider, tokenAmounts, receiverPrivateKey) {
console.log(`\n[~] Approving tokens for PartyPlanner contract...`); console.log(`\n[~] Approving tokens for PartyPlanner contract...`);
// Connect with receiver wallet for approvals
const receiverWallet = new ethers.Wallet(receiverPrivateKey, provider);
console.log(`[~] Using receiver wallet for approvals: ${receiverWallet.address}`);
for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) { for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) {
const tokenContract = new ethers.Contract(tokenInfo.address, ERC20ABI, wallet); const tokenContract = new ethers.Contract(tokenInfo.address, ERC20ABI, receiverWallet);
// Approve 1% more than needed to account for fees/slippage // Approve 1% more than needed to account for fees/slippage
const requiredAmount = tokenAmounts[symbol]; const requiredAmount = tokenAmounts[symbol];
const approvalAmount = requiredAmount.mul(101).div(100); // 1% buffer const approvalAmount = requiredAmount.mul(101).div(100); // 1% buffer
console.log(` [~] Approving ${symbol} (1% buffer)...`); console.log(` [~] Approving ${symbol} ${tokenInfo.address} ${approvalAmount} (1% buffer)...`);
try { try {
// USDT and some tokens require setting allowance to 0 before setting a new value // // USDT and some tokens require setting allowance to 0 before setting a new value
// Skip for BNB as it has a broken approve function // // Skip for BNB as it has a broken approve function
if (symbol !== 'BNB') { // if (symbol !== 'BNB') {
const resetTx = await tokenContract.approve(PARTY_PLANNER_ADDRESS, 0); // const resetTx = await tokenContract.approve(PARTY_PLANNER_ADDRESS, 0);
await resetTx.wait(); // await resetTx.wait();
console.log(` [+] ${symbol} allowance reset to 0`); // console.log(` [+] ${symbol} allowance reset to 0`);
} // }
const tx = await tokenContract.approve(PARTY_PLANNER_ADDRESS, approvalAmount); const tx = await tokenContract.approve(PARTY_PLANNER_ADDRESS, approvalAmount);
await tx.wait(); await tx.wait();
@@ -268,16 +319,16 @@ async function createPool(wallet, tokenAmounts) {
const tokenAddresses = Object.values(TEST_TOKENS).map(t => t.address); const tokenAddresses = Object.values(TEST_TOKENS).map(t => t.address);
const initialDeposits = Object.keys(TEST_TOKENS).map(symbol => tokenAmounts[symbol].toString()); const initialDeposits = Object.keys(TEST_TOKENS).map(symbol => tokenAmounts[symbol].toString());
// Set deadline to 1 hour from now // Set deadline to 5 minutes from now
const deadline = Math.floor(Date.now() / 1000) + 3600; const deadline = Math.floor(Date.now() / 1000) + 300;
console.log(`[~] Pool parameters:`); console.log(`[~] Pool parameters:`);
console.log(` Name: ${DEFAULT_POOL_PARAMS.name}`); console.log(` Name: ${DEFAULT_POOL_PARAMS.name}`);
console.log(` Symbol: ${DEFAULT_POOL_PARAMS.symbol}`); console.log(` Symbol: ${DEFAULT_POOL_PARAMS.symbol}`);
console.log(` Tokens: ${tokenAddresses.join(', ')}`); console.log(` Tokens: ${tokenAddresses.join(', ')}`);
console.log(` Swap Fees PPM: [${DEFAULT_POOL_PARAMS.swapFeesPpm.join(', ')}]`); console.log(` Swap Fees PPM: [${DEFAULT_POOL_PARAMS.swapFeesPpm.join(', ')}]`);
console.log(` Payer: ${wallet.address}`); console.log(` Payer (provides tokens): ${RECEIVER_ADDRESS}`);
console.log(` Receiver: ${RECEIVER_ADDRESS}`); console.log(` Receiver (gets LP tokens): ${wallet.address}`);
console.log(` Deadline: ${new Date(deadline * 1000).toISOString()}`); console.log(` Deadline: ${new Date(deadline * 1000).toISOString()}`);
// Build cast send command // Build cast send command
@@ -290,15 +341,14 @@ async function createPool(wallet, tokenAmounts) {
"[${DEFAULT_POOL_PARAMS.swapFeesPpm.join(',')}]" \ "[${DEFAULT_POOL_PARAMS.swapFeesPpm.join(',')}]" \
${DEFAULT_POOL_PARAMS.flashFeePpm} \ ${DEFAULT_POOL_PARAMS.flashFeePpm} \
${DEFAULT_POOL_PARAMS.stable} \ ${DEFAULT_POOL_PARAMS.stable} \
${wallet.address} \
${RECEIVER_ADDRESS} \ ${RECEIVER_ADDRESS} \
${wallet.address} \
"[${initialDeposits.join(',')}]" \ "[${initialDeposits.join(',')}]" \
${DEFAULT_POOL_PARAMS.initialLpAmount.toString()} \ ${DEFAULT_POOL_PARAMS.initialLpAmount.toString()} \
${deadline} \ ${deadline} \
--rpc-url http://localhost:8545 \ --rpc-url '${RPC_URL}' \
--from 0x12db90820dafed100e40e21128e40dcd4ff6b331 \ --from 0x12db90820dafed100e40e21128e40dcd4ff6b331 \
--trezor --mnemonic-index 0`; --trezor --mnemonic-index 0`
// --private-key ${PRIVATE_KEY};
console.log(`\n[~] Cast command:\n${castCommand}\n`); console.log(`\n[~] Cast command:\n${castCommand}\n`);
@@ -346,6 +396,7 @@ Example:
async function main() { async function main() {
console.log(`${'='.repeat(70)}`); console.log(`${'='.repeat(70)}`);
console.log(`Create Pool from Real-Time Prices`); console.log(`Create Pool from Real-Time Prices`);
console.log(`Network: ${NETWORK}`);
console.log(`${'='.repeat(70)}\n`); console.log(`${'='.repeat(70)}\n`);
// Parse command line arguments // Parse command line arguments
@@ -385,16 +436,23 @@ async function main() {
const tokenAmounts = calculateTokenAmounts(prices, usdAmount); const tokenAmounts = calculateTokenAmounts(prices, usdAmount);
// //
// // Step 3: Connect to wallet // // Step 3: Connect to wallet
console.log(`\n[~] Connecting to test wallet at ${RPC_URL}...`); console.log(`\n[~] Connecting to sender wallet at ${RPC_URL}...`);
const provider = new ethers.providers.JsonRpcProvider(RPC_URL); const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(PRIVATE_KEY, provider); const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
console.log(`[+] Connected. Using wallet: ${wallet.address}`); console.log(`[+] Connected. Using sender wallet: ${wallet.address}`);
console.log(`[+] Receiver wallet: ${RECEIVER_ADDRESS}`);
// //
// Step 4: Check balances // Step 4: Check balances
await checkBalances(provider, wallet, tokenAmounts); await checkBalances(provider, wallet, tokenAmounts, RECEIVER_ADDRESS);
// Step 5: Approve tokens // // // Step 5: Approve tokens
await approveTokens(wallet, tokenAmounts); // if (NETWORK === 'mockchain' && currentConfig.receiverPrivateKey) {
// // On mockchain, use receiver wallet for approvals
// await approveTokens(provider, tokenAmounts, currentConfig.receiverPrivateKey);
// } else if (NETWORK === 'mainnet') {
// // On mainnet, use the main wallet (payer and receiver are the same)
// await approveTokens(provider, tokenAmounts, PRIVATE_KEY);
// }
// Step 6: Create pool // Step 6: Create pool
await createPool(wallet, tokenAmounts); await createPool(wallet, tokenAmounts);