mirror of
https://github.com/hanzoskill/solana-skills.git
synced 2026-10-30 21:06:45 +00:00
539 lines
20 KiB
Python
539 lines
20 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Pump.fun Token Launch
|
|
Usage: python3 pumpfun.py launch --name "Token" --symbol "TKN" --image ./image.png [--buy 0.1]
|
|
|
|
Commands:
|
|
launch Launch a new token on pump.fun with optional dev buy
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import asyncio
|
|
import argparse
|
|
import struct
|
|
import json
|
|
import mimetypes
|
|
from pathlib import Path
|
|
from decimal import Decimal
|
|
from typing import Optional, Dict, Any, Tuple
|
|
|
|
try:
|
|
import aiohttp
|
|
import base58
|
|
from solders.keypair import Keypair
|
|
from solders.pubkey import Pubkey
|
|
from solders.instruction import Instruction, AccountMeta
|
|
from solders.system_program import ID as SYSTEM_PROGRAM_ID
|
|
from solders.transaction import VersionedTransaction
|
|
from solders.message import MessageV0
|
|
from solana.rpc.api import Client
|
|
from solana.rpc.commitment import Confirmed
|
|
from solana.rpc.types import TxOpts
|
|
from spl.token.instructions import get_associated_token_address
|
|
except ImportError as e:
|
|
print(f"Error: Required packages not installed. Missing: {e.name}")
|
|
print("Run: pip install solana solders base58 aiohttp spl-token")
|
|
sys.exit(1)
|
|
|
|
|
|
# Pump.fun constants
|
|
PUMP_PROGRAM_ID = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P")
|
|
PUMP_GLOBAL = Pubkey.from_string("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf")
|
|
PUMP_FEE_RECIPIENT = Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM")
|
|
PUMP_EVENT_AUTHORITY = Pubkey.from_string("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1")
|
|
TOKEN_2022_PROGRAM_ID = Pubkey.from_string("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb")
|
|
ASSOCIATED_TOKEN_PROGRAM_ID = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")
|
|
MAYHEM_PROGRAM_ID = Pubkey.from_string("MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e")
|
|
|
|
# Bonding curve constants
|
|
LAMPORTS_PER_SOL = 1_000_000_000
|
|
TOKEN_DECIMALS = 1_000_000 # 6 decimals
|
|
INITIAL_VIRTUAL_SOL = 30.0
|
|
|
|
# Discriminators
|
|
CREATE_V2_DISCRIMINATOR = bytes([214, 144, 76, 236, 95, 139, 49, 180])
|
|
BUY_DISCRIMINATOR = bytes.fromhex("66063d1201daebea")
|
|
|
|
# RPC
|
|
NETWORKS = {
|
|
"mainnet": "https://api.mainnet-beta.solana.com",
|
|
"devnet": "https://api.devnet.solana.com",
|
|
}
|
|
|
|
|
|
def get_keypair() -> Keypair:
|
|
"""Load keypair from environment variable."""
|
|
private_key = os.environ.get("SOLANA_PRIVATE_KEY")
|
|
if not private_key:
|
|
print("Error: SOLANA_PRIVATE_KEY environment variable not set.")
|
|
sys.exit(1)
|
|
try:
|
|
key_bytes = base58.b58decode(private_key)
|
|
return Keypair.from_bytes(key_bytes)
|
|
except Exception as e:
|
|
print(f"Error: Invalid private key format: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def get_client(network: str = "mainnet") -> Client:
|
|
"""Get Solana RPC client."""
|
|
rpc_url = os.environ.get("SOLANA_RPC_URL") or NETWORKS.get(network, NETWORKS["mainnet"])
|
|
return Client(rpc_url)
|
|
|
|
|
|
def derive_mint_authority() -> Pubkey:
|
|
"""Derive mint authority PDA."""
|
|
pda, _ = Pubkey.find_program_address([b"mint-authority"], PUMP_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_bonding_curve(mint: Pubkey) -> Pubkey:
|
|
"""Derive bonding curve PDA."""
|
|
pda, _ = Pubkey.find_program_address([b"bonding-curve", bytes(mint)], PUMP_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_associated_bonding_curve(bonding_curve: Pubkey, mint: Pubkey) -> Pubkey:
|
|
"""Derive associated bonding curve (ATA for Token-2022)."""
|
|
pda, _ = Pubkey.find_program_address(
|
|
[bytes(bonding_curve), bytes(TOKEN_2022_PROGRAM_ID), bytes(mint)],
|
|
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
)
|
|
return pda
|
|
|
|
|
|
def derive_event_authority() -> Pubkey:
|
|
"""Derive event authority PDA."""
|
|
pda, _ = Pubkey.find_program_address([b"__event_authority"], PUMP_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_global_params() -> Pubkey:
|
|
"""Derive global params PDA from Mayhem program."""
|
|
pda, _ = Pubkey.find_program_address([b"global-params"], MAYHEM_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_sol_vault() -> Pubkey:
|
|
"""Derive SOL vault PDA from Mayhem program."""
|
|
pda, _ = Pubkey.find_program_address([b"sol-vault"], MAYHEM_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_mayhem_state(mint: Pubkey) -> Pubkey:
|
|
"""Derive mayhem state PDA."""
|
|
pda, _ = Pubkey.find_program_address([b"mayhem-state", bytes(mint)], MAYHEM_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_mayhem_token_vault(mayhem_state: Pubkey, mint: Pubkey) -> Pubkey:
|
|
"""Derive mayhem token vault ATA."""
|
|
pda, _ = Pubkey.find_program_address(
|
|
[bytes(mayhem_state), bytes(TOKEN_2022_PROGRAM_ID), bytes(mint)],
|
|
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
)
|
|
return pda
|
|
|
|
|
|
def derive_user_ata(user: Pubkey, mint: Pubkey) -> Pubkey:
|
|
"""Derive user's ATA for Token-2022."""
|
|
pda, _ = Pubkey.find_program_address(
|
|
[bytes(user), bytes(TOKEN_2022_PROGRAM_ID), bytes(mint)],
|
|
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
)
|
|
return pda
|
|
|
|
|
|
def derive_creator_vault(creator: Pubkey) -> Pubkey:
|
|
"""Derive creator vault PDA."""
|
|
pda, _ = Pubkey.find_program_address([b"creator-vault", bytes(creator)], PUMP_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_global_volume_accumulator() -> Pubkey:
|
|
"""Return global volume accumulator address."""
|
|
return Pubkey.from_string("Hq2wp8uJ9jCPsYgNHex8RtqdvMPfVGoYwjvF1ATiwn2Y")
|
|
|
|
|
|
def derive_user_volume_accumulator(user: Pubkey) -> Pubkey:
|
|
"""Derive user volume accumulator PDA."""
|
|
pda, _ = Pubkey.find_program_address([b"user_volume_accumulator", bytes(user)], PUMP_PROGRAM_ID)
|
|
return pda
|
|
|
|
|
|
def derive_fee_config() -> Pubkey:
|
|
"""Return fee config address."""
|
|
return Pubkey.from_string("8Wf5TiAheLUqBrKXeYg2JtAFFMWtKdG2BSFgqUcPVwTt")
|
|
|
|
|
|
def derive_fee_program() -> Pubkey:
|
|
"""Return fee program address."""
|
|
return Pubkey.from_string("pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ")
|
|
|
|
|
|
def encode_string(s: str) -> bytes:
|
|
"""Encode string with length prefix (Borsh format)."""
|
|
encoded = s.encode('utf-8')
|
|
return struct.pack('<I', len(encoded)) + encoded
|
|
|
|
|
|
def create_create_v2_instruction(
|
|
mint: Pubkey,
|
|
user: Pubkey,
|
|
name: str,
|
|
symbol: str,
|
|
uri: str,
|
|
is_mayhem_mode: bool = False
|
|
) -> Instruction:
|
|
"""Create the createV2 instruction."""
|
|
bonding_curve = derive_bonding_curve(mint)
|
|
associated_bonding_curve = derive_associated_bonding_curve(bonding_curve, mint)
|
|
mayhem_state = derive_mayhem_state(mint)
|
|
mayhem_token_vault = derive_mayhem_token_vault(mayhem_state, mint)
|
|
|
|
accounts = [
|
|
AccountMeta(pubkey=mint, is_signer=True, is_writable=True),
|
|
AccountMeta(pubkey=derive_mint_authority(), is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=associated_bonding_curve, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=user, is_signer=True, is_writable=True),
|
|
AccountMeta(pubkey=SYSTEM_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=TOKEN_2022_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=ASSOCIATED_TOKEN_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=MAYHEM_PROGRAM_ID, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=derive_global_params(), is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=derive_sol_vault(), is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=mayhem_state, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=mayhem_token_vault, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=derive_event_authority(), is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=PUMP_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
]
|
|
|
|
# Build instruction data
|
|
data = bytearray()
|
|
data.extend(CREATE_V2_DISCRIMINATOR)
|
|
data.extend(encode_string(name))
|
|
data.extend(encode_string(symbol))
|
|
data.extend(encode_string(uri))
|
|
data.extend(bytes(user)) # creator pubkey
|
|
data.append(1 if is_mayhem_mode else 0) # is_mayhem_mode bool
|
|
|
|
return Instruction(PUMP_PROGRAM_ID, bytes(data), accounts)
|
|
|
|
|
|
def create_ata_instruction(
|
|
payer: Pubkey,
|
|
owner: Pubkey,
|
|
mint: Pubkey
|
|
) -> Instruction:
|
|
"""Create ATA instruction for Token-2022."""
|
|
ata = derive_user_ata(owner, mint)
|
|
|
|
accounts = [
|
|
AccountMeta(pubkey=payer, is_signer=True, is_writable=True),
|
|
AccountMeta(pubkey=ata, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=owner, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=SYSTEM_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=TOKEN_2022_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
]
|
|
|
|
return Instruction(ASSOCIATED_TOKEN_PROGRAM_ID, bytes(), accounts)
|
|
|
|
|
|
def create_buy_instruction(
|
|
user: Keypair,
|
|
mint: Pubkey,
|
|
amount: int,
|
|
max_sol_cost: int
|
|
) -> Instruction:
|
|
"""Create buy instruction for dev buy."""
|
|
bonding_curve = derive_bonding_curve(mint)
|
|
associated_bonding_curve = derive_associated_bonding_curve(bonding_curve, mint)
|
|
user_ata = derive_user_ata(user.pubkey(), mint)
|
|
creator_vault = derive_creator_vault(user.pubkey())
|
|
|
|
accounts = [
|
|
AccountMeta(pubkey=PUMP_GLOBAL, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=PUMP_FEE_RECIPIENT, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=mint, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=bonding_curve, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=associated_bonding_curve, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=user_ata, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=user.pubkey(), is_signer=True, is_writable=True),
|
|
AccountMeta(pubkey=SYSTEM_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=TOKEN_2022_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=creator_vault, is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=PUMP_PROGRAM_ID, is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=derive_global_volume_accumulator(), is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=derive_user_volume_accumulator(user.pubkey()), is_signer=False, is_writable=True),
|
|
AccountMeta(pubkey=derive_fee_config(), is_signer=False, is_writable=False),
|
|
AccountMeta(pubkey=derive_fee_program(), is_signer=False, is_writable=False),
|
|
]
|
|
|
|
# Build instruction data
|
|
data = bytearray()
|
|
data.extend(BUY_DISCRIMINATOR)
|
|
data.extend(struct.pack('<Q', amount)) # token amount
|
|
data.extend(struct.pack('<Q', max_sol_cost)) # max SOL cost
|
|
|
|
return Instruction(PUMP_PROGRAM_ID, bytes(data), accounts)
|
|
|
|
|
|
async def upload_to_ipfs(image_path: str, name: str, symbol: str, description: str) -> str:
|
|
"""
|
|
Upload image and metadata to IPFS via pump.fun API.
|
|
Returns the metadata URI.
|
|
"""
|
|
print("◎ Uploading to IPFS...")
|
|
|
|
# Read image file
|
|
image_path = Path(image_path)
|
|
if not image_path.exists():
|
|
raise Exception(f"Image file not found: {image_path}")
|
|
|
|
mime_type, _ = mimetypes.guess_type(str(image_path))
|
|
if not mime_type or not mime_type.startswith('image/'):
|
|
raise Exception(f"Invalid image file type: {mime_type}")
|
|
|
|
with open(image_path, 'rb') as f:
|
|
image_data = f.read()
|
|
|
|
async with aiohttp.ClientSession() as session:
|
|
# Step 1: Get presign URL for image
|
|
print(" Getting presign URL for image...")
|
|
async with session.get("https://pump.fun/api/ipfs-presign") as resp:
|
|
if resp.status != 200:
|
|
raise Exception(f"Failed to get presign URL: {await resp.text()}")
|
|
presign_data = await resp.json()
|
|
image_upload_url = presign_data["data"]
|
|
|
|
# Step 2: Upload image
|
|
print(f" Uploading image ({image_path.name})...")
|
|
form = aiohttp.FormData()
|
|
form.add_field('file', image_data, filename=image_path.name, content_type=mime_type)
|
|
form.add_field('network', 'public')
|
|
form.add_field('name', image_path.name)
|
|
|
|
async with session.post(image_upload_url, data=form, headers={"source": "sdk/file"}) as resp:
|
|
if resp.status != 200:
|
|
raise Exception(f"Failed to upload image: {await resp.text()}")
|
|
image_result = await resp.json()
|
|
image_cid = image_result["data"]["cid"]
|
|
image_ipfs_url = f"https://ipfs.io/ipfs/{image_cid}"
|
|
|
|
print(f" Image uploaded: {image_ipfs_url}")
|
|
|
|
# Step 3: Get presign URL for metadata
|
|
print(" Getting presign URL for metadata...")
|
|
async with session.get("https://pump.fun/api/ipfs-presign") as resp:
|
|
if resp.status != 200:
|
|
raise Exception(f"Failed to get presign URL: {await resp.text()}")
|
|
presign_data = await resp.json()
|
|
metadata_upload_url = presign_data["data"]
|
|
|
|
# Step 4: Upload metadata JSON
|
|
print(" Uploading metadata...")
|
|
metadata = {
|
|
"name": name,
|
|
"symbol": symbol,
|
|
"description": description,
|
|
"image": image_ipfs_url,
|
|
"showName": True,
|
|
"createdOn": "https://pump.fun"
|
|
}
|
|
metadata_json = json.dumps(metadata)
|
|
|
|
form = aiohttp.FormData()
|
|
form.add_field('file', metadata_json, filename='data.json', content_type='application/json')
|
|
form.add_field('network', 'public')
|
|
form.add_field('name', 'data.json')
|
|
|
|
async with session.post(metadata_upload_url, data=form) as resp:
|
|
if resp.status != 200:
|
|
raise Exception(f"Failed to upload metadata: {await resp.text()}")
|
|
metadata_result = await resp.json()
|
|
metadata_cid = metadata_result["data"]["cid"]
|
|
metadata_uri = f"https://ipfs.io/ipfs/{metadata_cid}"
|
|
|
|
print(f" Metadata uploaded: {metadata_uri}")
|
|
|
|
return metadata_uri
|
|
|
|
|
|
def calculate_tokens_for_sol(sol_amount: float) -> int:
|
|
"""Calculate expected tokens for SOL amount at initial curve state."""
|
|
# At initial state: virtual_sol = 30, k = 32190005730 * 10^6
|
|
k = 32190005730 * TOKEN_DECIMALS
|
|
initial_tokens = 1073000191 * TOKEN_DECIMALS
|
|
|
|
current_tokens = initial_tokens - (k / INITIAL_VIRTUAL_SOL)
|
|
new_virtual_sol = INITIAL_VIRTUAL_SOL + sol_amount
|
|
new_tokens = initial_tokens - (k / new_virtual_sol)
|
|
tokens_received = new_tokens - current_tokens
|
|
|
|
return int(max(0, tokens_received))
|
|
|
|
|
|
def cmd_launch(args):
|
|
"""Launch a new token on pump.fun."""
|
|
keypair = get_keypair()
|
|
client = get_client(args.network)
|
|
|
|
print(f"◎ Pump.fun Token Launch")
|
|
print(f" Creator: {keypair.pubkey()}")
|
|
print(f" Name: {args.name}")
|
|
print(f" Symbol: {args.symbol}")
|
|
print(f" Image: {args.image}")
|
|
if args.buy:
|
|
print(f" Dev buy: {args.buy} SOL")
|
|
|
|
# Upload to IPFS
|
|
try:
|
|
metadata_uri = asyncio.run(upload_to_ipfs(
|
|
args.image,
|
|
args.name,
|
|
args.symbol,
|
|
args.description or ""
|
|
))
|
|
except Exception as e:
|
|
print(f"\n❌ IPFS upload failed: {e}")
|
|
sys.exit(1)
|
|
|
|
# Use provided mint key or generate random
|
|
if args.mint_key:
|
|
try:
|
|
mint_keypair = Keypair.from_base58_string(args.mint_key)
|
|
print(f"\n◎ Using custom mint: {mint_keypair.pubkey()}")
|
|
except Exception as e:
|
|
print(f"\n❌ Invalid mint key: {e}")
|
|
sys.exit(1)
|
|
else:
|
|
mint_keypair = Keypair()
|
|
print(f"\n◎ Generated mint: {mint_keypair.pubkey()}")
|
|
|
|
mint = mint_keypair.pubkey()
|
|
|
|
# Build instructions
|
|
instructions = []
|
|
|
|
# Create token instruction
|
|
create_ix = create_create_v2_instruction(
|
|
mint=mint,
|
|
user=keypair.pubkey(),
|
|
name=args.name,
|
|
symbol=args.symbol,
|
|
uri=metadata_uri,
|
|
is_mayhem_mode=False
|
|
)
|
|
instructions.append(create_ix)
|
|
|
|
# Optional dev buy
|
|
if args.buy and args.buy > 0:
|
|
sol_amount = float(args.buy)
|
|
tokens_expected = calculate_tokens_for_sol(sol_amount)
|
|
max_sol_lamports = int(sol_amount * 1.1 * LAMPORTS_PER_SOL) # 10% slippage
|
|
|
|
# Create user's ATA first (required before buy)
|
|
ata_ix = create_ata_instruction(
|
|
payer=keypair.pubkey(),
|
|
owner=keypair.pubkey(),
|
|
mint=mint
|
|
)
|
|
instructions.append(ata_ix)
|
|
|
|
buy_ix = create_buy_instruction(
|
|
user=keypair,
|
|
mint=mint,
|
|
amount=tokens_expected,
|
|
max_sol_cost=max_sol_lamports
|
|
)
|
|
instructions.append(buy_ix)
|
|
print(f" Expected tokens: {tokens_expected / TOKEN_DECIMALS:,.0f}")
|
|
|
|
# Build and send transaction
|
|
print("\n◎ Sending transaction...")
|
|
|
|
try:
|
|
blockhash_resp = client.get_latest_blockhash(commitment=Confirmed)
|
|
recent_blockhash = blockhash_resp.value.blockhash
|
|
|
|
msg = MessageV0.try_compile(
|
|
payer=keypair.pubkey(),
|
|
instructions=instructions,
|
|
address_lookup_table_accounts=[],
|
|
recent_blockhash=recent_blockhash,
|
|
)
|
|
|
|
# Sign with both keypairs
|
|
signers = [keypair, mint_keypair]
|
|
tx = VersionedTransaction(msg, signers)
|
|
|
|
opts = TxOpts(skip_preflight=True, preflight_commitment=Confirmed)
|
|
result = client.send_transaction(tx, opts=opts)
|
|
signature = result.value
|
|
|
|
print(f"\n✅ Token launched!")
|
|
print(f"◎ Mint: {mint}")
|
|
print(f"◎ Signature: {signature}")
|
|
print(f"◎ Pump.fun: https://pump.fun/coin/{mint}")
|
|
print(f"◎ Explorer: https://solscan.io/tx/{signature}")
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ Transaction failed: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="Pump.fun Token Launch",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
pumpfun.py launch --name "My Token" --symbol "MTK" --image ./logo.png
|
|
pumpfun.py launch --name "My Token" --symbol "MTK" --image ./logo.png --buy 0.5
|
|
pumpfun.py launch --name "My Token" --symbol "MTK" --image ./logo.png --mint-key <base58_key>
|
|
|
|
Use --mint-key with a vanity address ending in 'pump' for more legit-looking tokens.
|
|
Generate vanity keys with tools like solana-keygen grind.
|
|
|
|
Environment variables (required):
|
|
SOLANA_PRIVATE_KEY Base58 private key
|
|
SOLANA_RPC_URL Custom RPC endpoint (optional, for faster execution)
|
|
"""
|
|
)
|
|
|
|
parser.add_argument(
|
|
"--network", "-n",
|
|
choices=["mainnet", "devnet"],
|
|
default="mainnet",
|
|
help="Solana network (default: mainnet)"
|
|
)
|
|
|
|
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
|
|
# launch command
|
|
launch_parser = subparsers.add_parser("launch", help="Launch a new token")
|
|
launch_parser.add_argument("--name", required=True, help="Token name")
|
|
launch_parser.add_argument("--symbol", required=True, help="Token symbol")
|
|
launch_parser.add_argument("--image", required=True, help="Path to token image")
|
|
launch_parser.add_argument("--description", "-d", default="", help="Token description")
|
|
launch_parser.add_argument("--buy", "-b", type=float, help="Dev buy amount in SOL (optional)")
|
|
launch_parser.add_argument("--mint-key", "-m", help="Base58 private key for mint (for vanity addresses ending with 'pump')")
|
|
launch_parser.set_defaults(func=cmd_launch)
|
|
|
|
args = parser.parse_args()
|
|
|
|
if not args.command:
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
args.func(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|