fix(examples): mint and buy script (#141)

* fix(examples): remove hardcoded token mints

* feat(core): update pump idls

* feat(examples): add track volume bool to mint script

* feat(examples): add extend acc instr to mint
This commit is contained in:
Anton
2025-10-25 15:01:04 +02:00
committed by GitHub
parent 25c376dd02
commit d64b51da44
9 changed files with 927 additions and 14 deletions
+438 -4
View File
@@ -739,7 +739,6 @@
},
{
"name": "fee_config",
"optional": true,
"pda": {
"seeds": [
{
@@ -803,7 +802,6 @@
},
{
"name": "fee_program",
"optional": true,
"address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
}
],
@@ -826,6 +824,422 @@
}
]
},
{
"name": "buy_exact_sol_in",
"docs": [
"Given a budget of spendable SOL, buy at least min_tokens_out",
"Account creation and fees will be deducted from the spendable SOL",
"",
"f(sol) = tokens, where tokens >= min_tokens_out and sol > rent + fees",
"",
"max_slippage = min_tokens_out = 1",
"",
"Make sure the sol budget is enough to cover creation of the following accounts (unless already created):",
"- creator_vault: rent.minimum_balance(SystemAccount::LEN)",
"- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)"
],
"discriminator": [
56,
252,
116,
8,
158,
223,
205,
95
],
"accounts": [
{
"name": "global",
"pda": {
"seeds": [
{
"kind": "const",
"value": [
103,
108,
111,
98,
97,
108
]
}
]
}
},
{
"name": "fee_recipient",
"writable": true
},
{
"name": "mint"
},
{
"name": "bonding_curve",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
98,
111,
110,
100,
105,
110,
103,
45,
99,
117,
114,
118,
101
]
},
{
"kind": "account",
"path": "mint"
}
]
}
},
{
"name": "associated_bonding_curve",
"writable": true,
"pda": {
"seeds": [
{
"kind": "account",
"path": "bonding_curve"
},
{
"kind": "const",
"value": [
6,
221,
246,
225,
215,
101,
161,
147,
217,
203,
225,
70,
206,
235,
121,
172,
28,
180,
133,
237,
95,
91,
55,
145,
58,
140,
245,
133,
126,
255,
0,
169
]
},
{
"kind": "account",
"path": "mint"
}
],
"program": {
"kind": "const",
"value": [
140,
151,
37,
143,
78,
36,
137,
241,
187,
61,
16,
41,
20,
142,
13,
131,
11,
90,
19,
153,
218,
255,
16,
132,
4,
142,
123,
216,
219,
233,
248,
89
]
}
}
},
{
"name": "associated_user",
"writable": true
},
{
"name": "user",
"writable": true,
"signer": true
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
},
{
"name": "token_program",
"address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
},
{
"name": "creator_vault",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
99,
114,
101,
97,
116,
111,
114,
45,
118,
97,
117,
108,
116
]
},
{
"kind": "account",
"path": "bonding_curve.creator",
"account": "BondingCurve"
}
]
}
},
{
"name": "event_authority",
"pda": {
"seeds": [
{
"kind": "const",
"value": [
95,
95,
101,
118,
101,
110,
116,
95,
97,
117,
116,
104,
111,
114,
105,
116,
121
]
}
]
}
},
{
"name": "program",
"address": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
},
{
"name": "global_volume_accumulator",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
103,
108,
111,
98,
97,
108,
95,
118,
111,
108,
117,
109,
101,
95,
97,
99,
99,
117,
109,
117,
108,
97,
116,
111,
114
]
}
]
}
},
{
"name": "user_volume_accumulator",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
117,
115,
101,
114,
95,
118,
111,
108,
117,
109,
101,
95,
97,
99,
99,
117,
109,
117,
108,
97,
116,
111,
114
]
},
{
"kind": "account",
"path": "user"
}
]
}
},
{
"name": "fee_config",
"pda": {
"seeds": [
{
"kind": "const",
"value": [
102,
101,
101,
95,
99,
111,
110,
102,
105,
103
]
},
{
"kind": "const",
"value": [
1,
86,
224,
246,
147,
102,
90,
207,
68,
219,
21,
104,
191,
23,
91,
170,
81,
137,
203,
151,
245,
210,
255,
59,
101,
93,
43,
182,
253,
109,
24,
176
]
}
],
"program": {
"kind": "account",
"path": "fee_program"
}
}
},
{
"name": "fee_program",
"address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
}
],
"args": [
{
"name": "spendable_sol_in",
"type": "u64"
},
{
"name": "min_tokens_out",
"type": "u64"
},
{
"name": "track_volume",
"type": {
"defined": {
"name": "OptionBool"
}
}
}
]
},
{
"name": "claim_token_incentives",
"discriminator": [
@@ -2568,7 +2982,6 @@
},
{
"name": "fee_config",
"optional": true,
"pda": {
"seeds": [
{
@@ -2632,7 +3045,6 @@
},
{
"name": "fee_program",
"optional": true,
"address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
}
],
@@ -3851,6 +4263,21 @@
{
"code": 6039,
"name": "InvalidIncentiveMint"
},
{
"code": 6040,
"name": "BuyNotEnoughSolToCoverRent",
"msg": "Buy: Not enough SOL to cover for rent exemption."
},
{
"code": 6041,
"name": "BuyNotEnoughSolToCoverFees",
"msg": "Buy: Not enough SOL to cover for fees."
},
{
"code": 6042,
"name": "BuySlippageBelowMinTokensOut",
"msg": "Slippage: Would buy less tokens than expected min_tokens_out"
}
],
"types": [
@@ -4573,6 +5000,9 @@
},
{
"name": "TradeEvent",
"docs": [
"ix_name: \"buy\" | \"sell\" | \"buy_exact_sol_in\""
],
"type": {
"kind": "struct",
"fields": [
@@ -4659,6 +5089,10 @@
{
"name": "last_update_timestamp",
"type": "i64"
},
{
"name": "ix_name",
"type": "string"
}
]
}
+449 -4
View File
@@ -620,7 +620,6 @@
},
{
"name": "fee_config",
"optional": true,
"pda": {
"seeds": [
{
@@ -684,7 +683,6 @@
},
{
"name": "fee_program",
"optional": true,
"address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
}
],
@@ -707,6 +705,434 @@
}
]
},
{
"name": "buy_exact_quote_in",
"docs": [
"Given a budget of spendable_quote_in, buy at least min_base_amount_out",
"Fees will be deducted from spendable_quote_in",
"",
"f(quote) = tokens, where tokens >= min_base_amount_out",
"",
"Make sure the payer has enough SOL to cover creation of the following accounts (unless already created):",
"- protocol_fee_recipient_token_account: rent.minimum_balance(TokenAccount::LEN)",
"- coin_creator_vault_ata: rent.minimum_balance(TokenAccount::LEN)",
"- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)"
],
"discriminator": [
198,
46,
21,
82,
180,
217,
232,
112
],
"accounts": [
{
"name": "pool"
},
{
"name": "user",
"writable": true,
"signer": true
},
{
"name": "global_config"
},
{
"name": "base_mint",
"relations": [
"pool"
]
},
{
"name": "quote_mint",
"relations": [
"pool"
]
},
{
"name": "user_base_token_account",
"writable": true
},
{
"name": "user_quote_token_account",
"writable": true
},
{
"name": "pool_base_token_account",
"writable": true,
"relations": [
"pool"
]
},
{
"name": "pool_quote_token_account",
"writable": true,
"relations": [
"pool"
]
},
{
"name": "protocol_fee_recipient"
},
{
"name": "protocol_fee_recipient_token_account",
"writable": true,
"pda": {
"seeds": [
{
"kind": "account",
"path": "protocol_fee_recipient"
},
{
"kind": "account",
"path": "quote_token_program"
},
{
"kind": "account",
"path": "quote_mint"
}
],
"program": {
"kind": "const",
"value": [
140,
151,
37,
143,
78,
36,
137,
241,
187,
61,
16,
41,
20,
142,
13,
131,
11,
90,
19,
153,
218,
255,
16,
132,
4,
142,
123,
216,
219,
233,
248,
89
]
}
}
},
{
"name": "base_token_program"
},
{
"name": "quote_token_program"
},
{
"name": "system_program",
"address": "11111111111111111111111111111111"
},
{
"name": "associated_token_program",
"address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
},
{
"name": "event_authority",
"pda": {
"seeds": [
{
"kind": "const",
"value": [
95,
95,
101,
118,
101,
110,
116,
95,
97,
117,
116,
104,
111,
114,
105,
116,
121
]
}
]
}
},
{
"name": "program",
"address": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"
},
{
"name": "coin_creator_vault_ata",
"writable": true,
"pda": {
"seeds": [
{
"kind": "account",
"path": "coin_creator_vault_authority"
},
{
"kind": "account",
"path": "quote_token_program"
},
{
"kind": "account",
"path": "quote_mint"
}
],
"program": {
"kind": "const",
"value": [
140,
151,
37,
143,
78,
36,
137,
241,
187,
61,
16,
41,
20,
142,
13,
131,
11,
90,
19,
153,
218,
255,
16,
132,
4,
142,
123,
216,
219,
233,
248,
89
]
}
}
},
{
"name": "coin_creator_vault_authority",
"pda": {
"seeds": [
{
"kind": "const",
"value": [
99,
114,
101,
97,
116,
111,
114,
95,
118,
97,
117,
108,
116
]
},
{
"kind": "account",
"path": "pool.coin_creator",
"account": "Pool"
}
]
}
},
{
"name": "global_volume_accumulator",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
103,
108,
111,
98,
97,
108,
95,
118,
111,
108,
117,
109,
101,
95,
97,
99,
99,
117,
109,
117,
108,
97,
116,
111,
114
]
}
]
}
},
{
"name": "user_volume_accumulator",
"writable": true,
"pda": {
"seeds": [
{
"kind": "const",
"value": [
117,
115,
101,
114,
95,
118,
111,
108,
117,
109,
101,
95,
97,
99,
99,
117,
109,
117,
108,
97,
116,
111,
114
]
},
{
"kind": "account",
"path": "user"
}
]
}
},
{
"name": "fee_config",
"pda": {
"seeds": [
{
"kind": "const",
"value": [
102,
101,
101,
95,
99,
111,
110,
102,
105,
103
]
},
{
"kind": "const",
"value": [
12,
20,
222,
252,
130,
94,
198,
118,
148,
37,
8,
24,
187,
101,
64,
101,
244,
41,
141,
49,
86,
213,
113,
180,
212,
248,
9,
12,
24,
233,
168,
99
]
}
],
"program": {
"kind": "account",
"path": "fee_program"
}
}
},
{
"name": "fee_program",
"address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
}
],
"args": [
{
"name": "spendable_quote_in",
"type": "u64"
},
{
"name": "min_base_amount_out",
"type": "u64"
},
{
"name": "track_volume",
"type": {
"defined": {
"name": "OptionBool"
}
}
}
]
},
{
"name": "claim_token_incentives",
"discriminator": [
@@ -2279,7 +2705,6 @@
},
{
"name": "fee_config",
"optional": true,
"pda": {
"seeds": [
{
@@ -2343,7 +2768,6 @@
},
{
"name": "fee_program",
"optional": true,
"address": "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"
}
],
@@ -3465,6 +3889,16 @@
{
"code": 6038,
"name": "InvalidIncentiveMint"
},
{
"code": 6039,
"name": "BuyNotEnoughQuoteTokensToCoverFees",
"msg": "buy: Not enough quote tokens to cover for fees."
},
{
"code": 6040,
"name": "BuySlippageBelowMinBaseAmountOut",
"msg": "buy: slippage - would buy less tokens than expected min_base_amount_out"
}
],
"types": [
@@ -3574,6 +4008,9 @@
},
{
"name": "BuyEvent",
"docs": [
"ix_name: \"buy\" | \"buy_exact_quote_in\""
],
"type": {
"kind": "struct",
"fields": [
@@ -3688,6 +4125,14 @@
{
"name": "last_update_timestamp",
"type": "i64"
},
{
"name": "min_base_amount_out",
"type": "u64"
},
{
"name": "ix_name",
"type": "string"
}
]
}
@@ -16,7 +16,7 @@ load_dotenv()
# Constants
RPC_URL: Final[str] = os.getenv("SOLANA_NODE_RPC_ENDPOINT")
TOKEN_MINT: Final[str] = "xWrzYY4c1LnbSkLrd2LDUg9vw7YtVyJhGmw7MABpump"
TOKEN_MINT: Final[str] = "YOUR_TOKEN_MINT_ADDRESS_HERE" # Replace with actual token mint address
PUMP_PROGRAM_ID: Final[Pubkey] = Pubkey.from_string(
"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
)
+1 -1
View File
@@ -12,7 +12,7 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
LAMPORTS_PER_SOL: Final[int] = 1_000_000_000
TOKEN_DECIMALS: Final[int] = 6
CURVE_ADDRESS: Final[str] = "6GXfUqrmPM4VdN1NoDZsE155jzRegJngZRjMkGyby7do"
CURVE_ADDRESS: Final[str] = "YOUR_BONDING_CURVE_ADDRESS_HERE" # Replace with actual bonding curve address
# Here and later all the discriminators are precalculated. See learning-examples/calculate_discriminator.py
EXPECTED_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 6966180631402821399)
@@ -40,7 +40,7 @@ IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)
load_dotenv()
TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address
# Configuration constants
@@ -40,7 +40,7 @@ IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)
load_dotenv()
TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address
# Configuration constants
@@ -40,7 +40,7 @@ IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)
load_dotenv()
TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address
# Configuration constants
@@ -40,7 +40,7 @@ IDL_PARSER = load_idl_parser("idl/raydium_launchlab_idl.json", verbose=True)
load_dotenv()
TOKEN_MINT_ADDRESS = Pubkey.from_string(
"MYcq5mUyoAtCfyDWYAWvioou3cgnYjnCvFd7U6fspot"
"YOUR_TOKEN_MINT_ADDRESS_HERE"
) # Replace with actual token mint address
# Configuration constants
+34
View File
@@ -70,6 +70,9 @@ TOKEN_DECIMALS: Final[int] = 6
# Discriminators
CREATE_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 8576854823835016728)
BUY_DISCRIMINATOR: Final[bytes] = struct.pack("<Q", 16927863322537952870)
EXTEND_ACCOUNT_DISCRIMINATOR: Final[bytes] = bytes(
[234, 102, 194, 203, 150, 72, 62, 229]
)
# From environment
RPC_ENDPOINT = os.environ.get("SOLANA_NODE_RPC_ENDPOINT")
@@ -194,6 +197,25 @@ def create_pump_create_instruction(
return Instruction(PUMP_PROGRAM, data, accounts)
def create_extend_account_instruction(
bonding_curve: Pubkey,
user: Pubkey,
) -> Instruction:
"""Create the extend_account instruction to expand bonding curve account size."""
accounts = [
AccountMeta(pubkey=bonding_curve, 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=PUMP_EVENT_AUTHORITY, is_signer=False, is_writable=False),
AccountMeta(pubkey=PUMP_PROGRAM, is_signer=False, is_writable=False),
]
# No arguments for extend_account instruction
data = EXTEND_ACCOUNT_DISCRIMINATOR
return Instruction(PUMP_PROGRAM, data, accounts)
def create_buy_instruction(
global_state: Pubkey,
fee_recipient: Pubkey,
@@ -205,6 +227,7 @@ def create_buy_instruction(
creator_vault: Pubkey,
token_amount: int,
max_sol_cost: int,
track_volume: bool = True,
) -> Instruction:
"""Create the buy instruction."""
accounts = [
@@ -242,10 +265,15 @@ def create_buy_instruction(
),
]
# Encode OptionBool for track_volume
# OptionBool: [0] = None, [1, 0] = Some(false), [1, 1] = Some(true)
track_volume_bytes = bytes([1, 1 if track_volume else 0])
data = (
BUY_DISCRIMINATOR
+ struct.pack("<Q", token_amount)
+ struct.pack("<Q", max_sol_cost)
+ track_volume_bytes
)
return Instruction(PUMP_PROGRAM, data, accounts)
@@ -317,6 +345,11 @@ async def main():
symbol=TOKEN_SYMBOL,
uri=TOKEN_URI,
),
# Extend bonding curve account (required for frontend visibility)
create_extend_account_instruction(
bonding_curve=bonding_curve,
user=payer.pubkey(),
),
# Create user ATA
create_idempotent_associated_token_account(
payer.pubkey(),
@@ -336,6 +369,7 @@ async def main():
creator_vault=creator_vault,
token_amount=expected_tokens,
max_sol_cost=max_sol_cost,
track_volume=True,
),
]