diff --git a/scripts/create_pool_from_prices.js b/scripts/create_pool_from_prices.js index f1ca2fb..74a6e00 100644 --- a/scripts/create_pool_from_prices.js +++ b/scripts/create_pool_from_prices.js @@ -6,55 +6,103 @@ import { ethers } from 'ethers'; import { readFile } from 'fs/promises'; +import { config } from 'dotenv'; import IPartyPlannerABI from "../src/contracts/IPartyPlannerABI.ts"; +// Load environment variables from .env-secret +config({ path: new URL('../.env-secret', import.meta.url).pathname }); + // ============================================================================ // CONFIGURATION // ============================================================================ -const RPC_URL = "https://eth-sepolia.g.alchemy.com/v2/sJ7rLYKJzdRqXUs9cOiLVvHN8aTI30dn" +const RPC_URL = process.env.MAINNET_RPC_URL; +const PRIVATE_KEY = process.env.PRIVATE_KEY; +const RECEIVER_ADDRESS = process.env.RECEIVER_ADDRESS; -// sepolia dev wallet -const PRIVATE_KEY = '89c8f2542b5ff7f3cf0b73255e0a8d79d89c2be598e7f272a275a380ff56a212'; +if (!RPC_URL || !PRIVATE_KEY || !RECEIVER_ADDRESS) { + console.error('[!] Missing required environment variables in .env-secret file'); + console.error(' Required: MAINNET_RPC_URL, PRIVATE_KEY, RECEIVER_ADDRESS'); + process.exit(1); +} -const RECEIVER_ADDRESS = '0xd3b310bd32d782f89eea49cb79656bcaccde7213'; - -// Test token addresses (mapping to real coins) +// Curated token list with real addresses (Ethereum Mainnet) const TEST_TOKENS = { - USDC: { - address: '0xCb2F4B07eFe0F06264AD28590aC7f03D5cdb0729', // USXD (mock) = USDC (real) - coingeckoId: 'usd-coin', - decimals: 6 + USDT: { + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', // Mainnet USDT + coingeckoId: 'tether', + decimals: 6, + feePpm: 400 // 0.00004 = 0.004% = 40 ppm }, - BTC: { - address: '0x19Ba3A189dc3DEbC07ADF7757dc8170702E91696', // BUTC (mock) = BTC (real) - coingeckoId: 'bitcoin', - decimals: 8 + USDC: { + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // Mainnet USDC + coingeckoId: 'usd-coin', + decimals: 6, + feePpm: 400 // 0.00004 = 0.004% = 40 ppm + }, + WBTC: { + address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', // Mainnet WBTC + coingeckoId: 'wrapped-bitcoin', + decimals: 8, + feePpm: 3000 // 0.00030 = 0.03% = 300 ppm }, WETH: { - address: '0xd406e1a6b028D17b72f826E45bF36BB8Ad4077dB', // WTETH (mock) = WETH (real) + address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', // Mainnet WETH coingeckoId: 'weth', - decimals: 18 + decimals: 18, + feePpm: 3500 // 0.00035 = 0.035% = 350 ppm + }, + BNB: { + address: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', // Mainnet BNB (Binance-Peg) + coingeckoId: 'binancecoin', + decimals: 18, + feePpm: 4500 // 0.00045 = 0.045% = 450 ppm + }, + WSOL: { + address: '0xD31a59c85aE9D8edEFeC411D448f90841571b89c', // Mainnet Wormhole Wrapped SOL + coingeckoId: 'solana', + decimals: 9, + feePpm: 9500 // 0.00095 = 0.095% = 950 ppm + }, + TRX: { + address: '0x50327c6c5a14DCaDE707ABad2E27eB517df87AB5', // Mainnet TRX (Wrapped) + coingeckoId: 'tron', + decimals: 6, + feePpm: 9500 // 0.00095 = 0.095% = 950 ppm + }, + AAVE: { + address: '0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9', // Mainnet AAVE + coingeckoId: 'aave', + decimals: 18, + feePpm: 14500 // 0.00145 = 0.145% = 1450 ppm + }, + PEPE: { + address: '0x6982508145454Ce325dDbE47a25d4ec3d2311933', // Mainnet PEPE + coingeckoId: 'pepe', + decimals: 18, + feePpm: 21500 // 0.00215 = 0.215% = 2150 ppm + }, + SHIB: { + address: '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE', // Mainnet SHIB + coingeckoId: 'shiba-inu', + decimals: 18, + feePpm: 21500 // 0.00215 = 0.215% = 2150 ppm } }; // Default pool parameters const DEFAULT_POOL_PARAMS = { - name: 'Balanced Portfolio Pool', - symbol: 'BPP', - kappa: ethers.BigNumber.from('100000000000000000'), // 0.1 * 1e18 = 1e17 - swapFeesPpm: [ - 50, // 0.0050% - 2450, // 0.2450% - 2950, // 0.2950% - ], + name: 'Staging LP Pool', + symbol: 'SLP', + kappa: ethers.BigNumber.from('10000000000000000'), // 0.01 * 1e18 + swapFeesPpm: Object.values(TEST_TOKENS).map(t => t.feePpm), flashFeePpm: 5, // 0.0005% stable: false, - initialLpAmount: ethers.BigNumber.from('1000000000000000000') // 1e18 + initialLpAmount: ethers.utils.parseUnits('100', 18) // 100 USD in 18 decimals }; // Input amount in USD -const INPUT_USD_AMOUNT = 100; +const INPUT_USD_AMOUNT = 1; // ============================================================================ @@ -62,7 +110,7 @@ const INPUT_USD_AMOUNT = 100; // ============================================================================ const chainInfoData = JSON.parse(await readFile(new URL('../src/contracts/liqp-deployments.json', import.meta.url), 'utf-8')); -const PARTY_PLANNER_ADDRESS = chainInfoData['11155111'].v1.PartyPlanner; +const PARTY_PLANNER_ADDRESS = chainInfoData['1'].v1.PartyPlanner; const ERC20ABI = [ { "type": "function", "name": "balanceOf", "stateMutability": "view", "inputs": [{ "name": "account", "type": "address" }], "outputs": [{ "name": "", "type": "uint256" }] }, @@ -92,20 +140,19 @@ async function fetchCoinGeckoPrices() { const data = await response.json(); - const prices = { - USDC: data['usd-coin']?.usd || 1, - BTC: data['bitcoin']?.usd || 0, - WETH: data['weth']?.usd || 0 - }; + const prices = {}; - if (prices.BTC === 0 || prices.WETH === 0) { - throw new Error('Failed to fetch valid prices from CoinGecko'); + for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) { + prices[symbol] = data[tokenInfo.coingeckoId]?.usd || 0; + if (prices[symbol] === 0) { + throw new Error(`Failed to fetch valid price for ${symbol}`); + } } console.log(`[+] Prices fetched successfully:`); - console.log(` USDC: $${prices.USDC}`); - console.log(` BTC: $${prices.BTC.toLocaleString()}`); - console.log(` WETH: $${prices.WETH.toLocaleString()}`); + for (const [symbol, price] of Object.entries(prices)) { + console.log(` ${symbol.padEnd(6)}: $${price.toLocaleString()}`); + } return prices; } catch (error) { @@ -118,28 +165,25 @@ async function fetchCoinGeckoPrices() { * Calculate token amounts based on equal USD distribution */ function calculateTokenAmounts(prices, usdAmount) { - const usdPerToken = usdAmount / 3; // Equally distribute among 3 tokens + const tokenCount = Object.keys(TEST_TOKENS).length; + const usdPerToken = usdAmount / tokenCount; // Equally distribute among all tokens - // Calculate raw amounts - const usdcAmount = usdPerToken / prices.USDC; - const btcAmount = usdPerToken / prices.BTC; - const wethAmount = usdPerToken / prices.WETH; - - // Convert to BigNumber with proper decimals - const usdcAmountBN = ethers.utils.parseUnits(usdcAmount.toFixed(TEST_TOKENS.USDC.decimals), TEST_TOKENS.USDC.decimals); - const btcAmountBN = ethers.utils.parseUnits(btcAmount.toFixed(TEST_TOKENS.BTC.decimals), TEST_TOKENS.BTC.decimals); - const wethAmountBN = ethers.utils.parseUnits(wethAmount.toFixed(TEST_TOKENS.WETH.decimals), TEST_TOKENS.WETH.decimals); + const tokenAmounts = {}; console.log(`\n[~] Calculated token amounts for $${usdAmount} ($${usdPerToken.toFixed(2)} per token):`); - console.log(` USDC: ${usdcAmount.toFixed(6)} USDC (${usdcAmountBN.toString()} wei)`); - console.log(` BTC: ${btcAmount.toFixed(8)} BTC (${btcAmountBN.toString()} wei)`); - console.log(` WETH: ${wethAmount.toFixed(8)} WETH (${wethAmountBN.toString()} wei)`); - return { - USDC: usdcAmountBN, - BTC: btcAmountBN, - WETH: wethAmountBN - }; + for (const [symbol, tokenInfo] of Object.entries(TEST_TOKENS)) { + // Calculate raw amount + const rawAmount = usdPerToken / prices[symbol]; + + // Convert to BigNumber with proper decimals + const amountBN = ethers.utils.parseUnits(rawAmount.toFixed(tokenInfo.decimals), tokenInfo.decimals); + + tokenAmounts[symbol] = amountBN; + console.log(` ${symbol.padEnd(6)}: ${rawAmount.toFixed(tokenInfo.decimals)} (${amountBN.toString()} wei)`); + } + + return tokenAmounts; } /** @@ -194,6 +238,14 @@ async function approveTokens(wallet, tokenAmounts) { console.log(` [~] Approving ${symbol} (1% buffer)...`); try { + // USDT and some tokens require setting allowance to 0 before setting a new value + // Skip for BNB as it has a broken approve function + if (symbol !== 'BNB') { + const resetTx = await tokenContract.approve(PARTY_PLANNER_ADDRESS, 0); + await resetTx.wait(); + console.log(` [+] ${symbol} allowance reset to 0`); + } + const tx = await tokenContract.approve(PARTY_PLANNER_ADDRESS, approvalAmount); await tx.wait(); console.log(` [+] ${symbol} approved (tx: ${tx.hash})`); @@ -213,17 +265,8 @@ async function createPool(wallet, tokenAmounts) { console.log(`\n[~] Creating new pool...`); // Prepare parameters - const tokenAddresses = [ - TEST_TOKENS.USDC.address, - TEST_TOKENS.BTC.address, - TEST_TOKENS.WETH.address - ]; - - const initialDeposits = [ - tokenAmounts.USDC.toString(), - tokenAmounts.BTC.toString(), - tokenAmounts.WETH.toString() - ]; + const tokenAddresses = Object.values(TEST_TOKENS).map(t => t.address); + const initialDeposits = Object.keys(TEST_TOKENS).map(symbol => tokenAmounts[symbol].toString()); // Set deadline to 1 hour from now const deadline = Math.floor(Date.now() / 1000) + 3600; @@ -252,8 +295,10 @@ async function createPool(wallet, tokenAmounts) { "[${initialDeposits.join(',')}]" \ ${DEFAULT_POOL_PARAMS.initialLpAmount.toString()} \ ${deadline} \ - --rpc-url ${RPC_URL} \ - --private-key ${PRIVATE_KEY}`; + --rpc-url http://localhost:8545 \ + --from 0x12db90820dafed100e40e21128e40dcd4ff6b331 \ + --trezor --mnemonic-index 0`; + // --private-key ${PRIVATE_KEY}; console.log(`\n[~] Cast command:\n${castCommand}\n`); @@ -338,13 +383,13 @@ async function main() { // Step 2: Calculate token amounts 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}...`); const provider = new ethers.providers.JsonRpcProvider(RPC_URL); const wallet = new ethers.Wallet(PRIVATE_KEY, provider); console.log(`[+] Connected. Using wallet: ${wallet.address}`); - + // // Step 4: Check balances await checkBalances(provider, wallet, tokenAmounts);