diff --git a/learning-examples/mint_and_buy.py b/learning-examples/mint_and_buy.py new file mode 100644 index 0000000..4e5f23e --- /dev/null +++ b/learning-examples/mint_and_buy.py @@ -0,0 +1,305 @@ +import asyncio +import os +import struct +from typing import Final + +import base58 +from dotenv import load_dotenv +from solana.rpc.async_api import AsyncClient +from solana.rpc.commitment import Confirmed +from solana.rpc.types import TxOpts +from solders.compute_budget import set_compute_unit_limit, set_compute_unit_price +from solders.instruction import AccountMeta, Instruction +from solders.keypair import Keypair +from solders.message import Message +from solders.pubkey import Pubkey +from solders.transaction import Transaction +from spl.token.instructions import ( + create_idempotent_associated_token_account, + get_associated_token_address, +) + +# Configuration for the token to be created +TOKEN_NAME = "Test Token" +TOKEN_SYMBOL = "TEST" +TOKEN_URI = "https://example.com/token.json" +BUY_AMOUNT_SOL = 0.001 # Amount of SOL to spend on buying +MAX_SLIPPAGE = 0.3 # 30% slippage +PRIORITY_FEE_MICROLAMPORTS = 37_037 # Priority fee in microlamports +COMPUTE_UNIT_LIMIT = 250_000 # Compute unit limit for the transaction + +load_dotenv() + +# Global constants from existing codebase +PUMP_PROGRAM: Final[Pubkey] = Pubkey.from_string("6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P") +PUMP_GLOBAL: Final[Pubkey] = Pubkey.from_string("4wTV1YmiEkRvAtNtsSGPtUrqRYQMe5SKy2uB4Jjaxnjf") +PUMP_EVENT_AUTHORITY: Final[Pubkey] = Pubkey.from_string("Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1") +PUMP_FEE: Final[Pubkey] = Pubkey.from_string("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM") +PUMP_MINT_AUTHORITY: Final[Pubkey] = Pubkey.from_string("TSLvdd1pWpHVjahSpsvCXUbgwsL3JAcvokwaKt1eokM") + +SYSTEM_PROGRAM: Final[Pubkey] = Pubkey.from_string("11111111111111111111111111111111") +SYSTEM_TOKEN_PROGRAM: Final[Pubkey] = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA") +SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM: Final[Pubkey] = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") +SYSTEM_RENT: Final[Pubkey] = Pubkey.from_string("SysvarRent111111111111111111111111111111111") +METAPLEX_TOKEN_METADATA: Final[Pubkey] = Pubkey.from_string("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s") + +LAMPORTS_PER_SOL: Final[int] = 1_000_000_000 +TOKEN_DECIMALS: Final[int] = 6 + +# Discriminators +CREATE_DISCRIMINATOR: Final[bytes] = struct.pack(" tuple[Pubkey, int]: + """Find the bonding curve PDA for a mint.""" + return Pubkey.find_program_address( + [b"bonding-curve", bytes(mint)], + PUMP_PROGRAM + ) + + +def find_associated_bonding_curve(mint: Pubkey, bonding_curve: Pubkey) -> Pubkey: + """Find the associated bonding curve token account.""" + derived_address, _ = Pubkey.find_program_address( + [ + bytes(bonding_curve), + bytes(SYSTEM_TOKEN_PROGRAM), + bytes(mint), + ], + SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM, + ) + return derived_address + + +def find_metadata_address(mint: Pubkey) -> Pubkey: + """Find the metadata PDA for a mint.""" + derived_address, _ = Pubkey.find_program_address( + [ + b"metadata", + bytes(METAPLEX_TOKEN_METADATA), + bytes(mint), + ], + METAPLEX_TOKEN_METADATA, + ) + return derived_address + + +def find_creator_vault(creator: Pubkey) -> Pubkey: + """Find the creator vault PDA.""" + derived_address, _ = Pubkey.find_program_address( + [ + b"creator-vault", + bytes(creator) + ], + PUMP_PROGRAM, + ) + return derived_address + + +def create_pump_create_instruction( + mint: Pubkey, + mint_authority: Pubkey, + bonding_curve: Pubkey, + associated_bonding_curve: Pubkey, + global_state: Pubkey, + metadata: Pubkey, + user: Pubkey, + creator: Pubkey, + name: str, + symbol: str, + uri: str, +) -> Instruction: + """Create the pump.fun create instruction.""" + accounts = [ + AccountMeta(pubkey=mint, is_signer=True, is_writable=True), + AccountMeta(pubkey=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=global_state, is_signer=False, is_writable=False), + AccountMeta(pubkey=METAPLEX_TOKEN_METADATA, is_signer=False, is_writable=False), + AccountMeta(pubkey=metadata, is_signer=False, is_writable=True), + AccountMeta(pubkey=user, is_signer=True, is_writable=True), + AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), + AccountMeta(pubkey=SYSTEM_TOKEN_PROGRAM, is_signer=False, is_writable=False), + AccountMeta(pubkey=SYSTEM_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM, is_signer=False, is_writable=False), + AccountMeta(pubkey=SYSTEM_RENT, is_signer=False, is_writable=False), + AccountMeta(pubkey=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False), + AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False), + ] + + # Encode string as length-prefixed + def encode_string(s: str) -> bytes: + encoded = s.encode('utf-8') + return struct.pack(" bytes: + return bytes(pubkey) + + data = ( + CREATE_DISCRIMINATOR + + encode_string(name) + + encode_string(symbol) + + encode_string(uri) + + encode_pubkey(creator) + ) + + return Instruction(PUMP_PROGRAM, data, accounts) + + +def create_buy_instruction( + global_state: Pubkey, + fee_recipient: Pubkey, + mint: Pubkey, + bonding_curve: Pubkey, + associated_bonding_curve: Pubkey, + associated_user: Pubkey, + user: Pubkey, + creator_vault: Pubkey, + token_amount: int, + max_sol_cost: int, +) -> Instruction: + """Create the buy instruction.""" + accounts = [ + AccountMeta(pubkey=global_state, is_signer=False, is_writable=False), + AccountMeta(pubkey=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=associated_user, is_signer=False, is_writable=True), + AccountMeta(pubkey=user, is_signer=True, is_writable=True), + AccountMeta(pubkey=SYSTEM_PROGRAM, is_signer=False, is_writable=False), + AccountMeta(pubkey=SYSTEM_TOKEN_PROGRAM, 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, is_signer=False, is_writable=False), + ] + + data = ( + BUY_DISCRIMINATOR + + struct.pack("