debpf
pump.fun / instructions

Instruction

multi_hop_curve_swap

ProvenHidden

This instruction isn't in the published IDL. It routes a token through a chain of pump "v3" bonding curves in one call. You pass an input amount and one fee-flag byte per hop. Bit 0 of the byte turns the protocol fee (95 bps) on and bit 1 turns the creator fee (30 bps) on, so the caller decides which fees each hop pays. Each hop sells or buys depending on whether the incoming mint is that curve's base or quote mint. The final output can go to any destination token account. There is no minimum-output (slippage) check, so the calling router must enforce slippage. The buy path and the multi-hop token routing come from reading the bytecode only. The single mainnet sample was a one-hop sell with creator fee only.

Discriminator
0xe19a7516d756f667
Calls in the sample
1
Reproduced, seen by the agent
1 / 1
Reproduced, never shown to the agent
—

1 of 1 real mainnet calls reproduced exactly. Every SOL and token movement the model predicted matched the chain to the last unit. Unseen calls were never shown to the agent.

Bytecodefn_24670
24670mov64 r8, r5
24671mov64 r7, r4
24672mov64 r9, r3
24673stxdw [r10-400], r2
24674mov64 r6, r1
24675lddw r1, 0x1ae430 ; "Instruction: MultiHopCurveSwap"
24677mov64 r2, 0x1e
24678call sol_log_
24679ldxdw r1, [r8-4088]
2467ajlt r1, 0x8, +135 ; -> 24702
2467bldxdw r2, [r8-4096]
2467cadd64 r1, -0x8
2467dldxdw r3, [r2+0]
2467estxdw [r10-408], r3
2467fstxdw [r10-328], r1
24680add64 r2, 0x8
24681stxdw [r10-336], r2
24682mov64 r1, r10
24683add64 r1, -0xc0
24684mov64 r2, r10
24685add64 r2, -0x150
24686call fn_02387
24687ldxdw r8, [r10-184]
24688ldxdw r2, [r10-192]
24689lddw r1, 0x8000000000000000
2468bjeq r2, r1, +122 ; -> 24706
2468cstxdw [r10-424], r2
2468dstxdw [r10-416], r6
2468eldxdw r1, [r10-176]
2468fstxdw [r10-432], r1
24690stb [r10-353], 0xff
24691sth [r10-355], 0xffff
24692stxdw [r10-344], r7
24693stxdw [r10-352], r9
24694mov64 r1, r10
24695add64 r1, -0x163
24696stxdw [r10-4088], r1
24697mov64 r1, r10
24698add64 r1, -0xc0
24699mov64 r3, r10
2469aadd64 r3, -0x160
2469bmov64 r5, r10
2469cldxdw r2, [r10-400]
2469dcall fn_1aaa2
2469eldxdw r9, [r10-184]
2469fldxdw r7, [r10-192]
246a0ldxb r6, [r10-54]
246a1jne r6, 0x2, +1 ; -> 246a3
246a2ja +118 ; -> 24719
246a3mov64 r1, r10
246a4add64 r1, -0x140
246a5mov64 r2, r10
246a6add64 r2, -0xb0
246a7mov64 r3, 0x7a
246a8call fn_35367
246a9ldxw r1, [r10-53]
246aastxw [r10-197], r1
246abldxb r1, [r10-49]
246acstxb [r10-193], r1
246adstxb [r10-198], r6
246aestxdw [r10-328], r9
246afstxdw [r10-336], r7
246b0ldxh r1, [r10-355]
246b1stxh [r10-160], r1
246b2ldxb r1, [r10-353]
246b3stxb [r10-158], r1
246b4ldxdw r1, [r10-344]
246b5stxdw [r10-168], r1
246b6ldxdw r1, [r10-352]
246b7stxdw [r10-176], r1
246b8mov64 r1, r10
246b9add64 r1, -0x150
246bastxdw [r10-184], r1
246bbldxdw r6, [r10-400]
246bcstxdw [r10-192], r6
246bdldxdw r1, [r10-432]
246bestxdw [r10-8], r1
246bfstxdw [r10-16], r8
246c0ldxdw r1, [r10-424]
246c1stxdw [r10-24], r1
246c2mov64 r1, r10
246c3add64 r1, -0x30
246c4mov64 r2, r10
246c5add64 r2, -0xc0
246c6mov64 r4, r10
246c7add64 r4, -0x18
246c8ldxdw r3, [r10-408]
246c9call fn_0a8d6
246caldxdw r7, [r10-40]
246cbldxdw r1, [r10-48]
246ccmov64 r8, r6
246cdjne r1, 0x0, +77 ; -> 2471b
246celddw r1, 0x300000000
246d0ldxdw r2, [r1+0]
246d1mov64 r1, r2
246d2add64 r1, -0x100
246d3mov64 r3, 0x0
246d4mov64 r4, 0x1
246d5jgt r1, r2, +1 ; -> 246d7
246d6mov64 r4, 0x0
246d7ldxdw r6, [r10-416]
246d8jne r4, 0x0, +1 ; -> 246da
246d9mov64 r3, r1
246dalddw r1, 0x300007f00
246dcjeq r2, 0x0, +1 ; -> 246de
246ddmov64 r1, r3
246delddw r2, 0x300000008
246e0jlt r1, r2, +74 ; -> 2472b
246e1ldxdw r2, [r10-32]
246e2lddw r3, 0x300000000
246e4stxdw [r3+0], r1
246e5stxdw [r1+8], r2
246e6stxdw [r1+0], r7
246e7mov64 r2, 0x10
246e8call fn_32db9
246e9mov64 r1, r10
246eaadd64 r1, -0x178
246ebmov64 r2, r10
246ecadd64 r2, -0x150
246edmov64 r3, r8
246eecall fn_1adf7
246efldxdw r1, [r10-224]
246f0ldxdw r2, [r10-232]
246f1ldxdw r3, [r2+0]
246f2add64 r3, -0x1
246f3ldxdw r9, [r10-368]
246f4ldxdw r7, [r10-376]
246f5stxdw [r2+0], r3
246f6jne r3, 0x0, +3 ; -> 246fa
246f7ldxdw r3, [r2+8]
246f8add64 r3, -0x1
246f9stxdw [r2+8], r3
246faldxdw r2, [r1+0]
246fbadd64 r2, -0x1
246fcstxdw [r1+0], r2
246fdjne r2, 0x0, +24 ; -> 24716
246feldxdw r2, [r1+8]
246ffadd64 r2, -0x1
24700stxdw [r1+8], r2
24701ja +20 ; -> 24716
24702lddw r1, 0x1b3c60
24704call fn_33e71
24705mov64 r8, r0
24706mov64 r1, r8
24707and64 r1, 0x3
24708mov64 r2, r1
24709add64 r2, -0x2
2470ajlt r2, 0x2, +5 ; -> 24710
2470bjeq r1, 0x0, +4 ; -> 24710
2470cldxdw r1, [r8-1]
2470dldxdw r2, [r8+7]
2470eldxdw r2, [r2+0]
2470fcallx r2
24710mov64 r1, r10
24711add64 r1, -0x188
24712mov64 r2, 0x66
24713call fn_32b09
24714ldxdw r9, [r10-384]
24715ldxdw r7, [r10-392]
24716stxdw [r6+8], r9
24717stxdw [r6+0], r7
24718exit
24719ldxdw r6, [r10-416]
2471aja -5 ; -> 24716
2471bldxdw r1, [r10-224]
2471cldxdw r9, [r10-32]
2471dldxdw r2, [r10-232]
2471eldxdw r3, [r2+0]
2471fadd64 r3, -0x1
24720stxdw [r2+0], r3
24721jne r3, 0x0, +3 ; -> 24725
24722ldxdw r3, [r2+8]
24723add64 r3, -0x1
24724stxdw [r2+8], r3
24725ldxdw r2, [r1+0]
24726add64 r2, -0x1
24727stxdw [r1+0], r2
24728ldxdw r6, [r10-416]
24729jne r2, 0x0, -20 ; -> 24716
2472aja -45 ; -> 246fe
2472bmov64 r1, 0x1
2472cmov64 r2, 0x100
2472dcall fn_342d8
Reconstructedmulti_hop_curve_swap
// discriminator e19a7516d756f667 — NOT in the published IDL
// args: amount_in: u64, fee_flags: Vec<u8> (one byte per hop; bit0 = charge protocol fee, bit1 = charge creator fee)
// accounts: 0 global, 1 fee_config, 2 pump_amm_global_config, 3 user (signer), 4 source_authority (signer),
// 5 source (token acct), 6 destination (token acct, any owner), 7 buyback_fee_recipient,
// 8 token_program, 9 token_2022_program, 10 system_program, 11 event_authority, 12 program
// remaining_accounts: 5 per hop = [base_mint, quote_mint, bonding_curve, curve_base_vault, curve_quote_vault]
fn multi_hop_curve_swap(ctx, amount_in: u64, fee_flags: Vec<u8>) -> Result<(u64, u64)> {
msg!("Instruction: MultiHopCurveSwap");
require!(amount_in != 0, 6020); // pc a8d8
let rem = ctx.remaining_accounts;
require!(rem.len() >= 5 && rem.len() % 5 == 0, 6027); // pc a8da
let hops = rem.len() / 5;
require!(fee_flags.len() == hops, 6106); // pc a8e2
require!(fee_flags.iter().all(|f| *f < 4), 6106); // pc a8eb
let mut amount = amount_in;
let mut in_mint = ctx.source.mint;
let mut total = 0u64;
for (i, hop) in rem.chunks(5).enumerate() {
let hop = HopAccounts::try_from(hop, bump)?; // fn_1ae57 (curve PDA "bonding-curve")
// fn_0b251:
require!(in_mint == hop.base_mint || in_mint == hop.quote_mint, 6098); // pc b442
let protocol_on = fee_flags[i] & 1 != 0; // pc b40d (also validates buyback accounts via fn_0f532)
let creator_on = fee_flags[i] & 2 != 0; // pc b267
let curve = &mut hop.bonding_curve; // v3 curve: virtual_token @+64, virtual_quote @+72
let mcap = curve_market_cap(curve); // used to pick fee tier
let (p_bps, c_bps) = select_fee_bps(fee_config, quote_mint, mcap); // fn_109ae: 95 / 30 today
if in_mint == hop.base_mint { // SELL base -> quote
let gross = (curve.vq as u128 * amount as u128 / (curve.vt + amount) as u128) as u64; // fn_1341e, floor
let pf = if protocol_on { ceil(gross * p_bps / 10000) } else { 0 };
let cf = if creator_on && curve.creator != default { ceil(gross * c_bps / 10000) } else { 0 };
let net = gross.checked_sub(pf + cf).ok_or(6024)?; // fn_0f8d7
curve.vt += amount; curve.vq -= gross; curve.real_* updated; // fn_0b027 / fn_135f3
transfer_checked(input_holder -> curve_base_vault, amount); // first hop: source signed by source_authority
let buyback = split_buyback(pf, global.buyback_bps /*5000*/); // to buyback_fee_recipient
// pf - buyback and cf stay in curve_quote_vault, accrued on the curve (swept later)
transfer_checked(curve_quote_vault -> next_holder, net); // curve PDA signs; last hop -> destination
amount = net; in_mint = hop.quote_mint;
} else { // BUY quote -> base
let (x, pf, cf) = v3_buy_exact_quote_in_split(amount, p_bps*protocol_on, c_bps*creator_on); // fn_0f91f
let out = tokens_out_for_quote_in(curve, x); // fn_1347e ~ vt*(x-1)/(vq+x-1)
transfer quote in, base `out` out; amount = out; in_mint = hop.base_mint;
}
emit!(TradeEvent{ ix_name: "multi_hop_swap", fee_bps, fee, creator_fee_bps, creator_fee, quote_amount, reserves, .. });
total = total.checked_add(hop_value)?;
}
require!(in_mint == ctx.destination.mint, 6098); // pc a9e1
set_return_data((amount, total)); // NO min-out / slippage check here
Ok(())
}

Replay model

The executable model the agent wrote. Every tally above comes from running it against mainnet.

function rd(d,o){let x=0n;for(let i=7;i>=0;i--)x=(x<<8n)|BigInt(d[o+i]);return x;}
function model(ctx){
const d=ctx.data, A=ctx.accounts;
let amount=rd(d,8);
const n=Number(d[16]|(d[17]<<8)|(d[18]<<16)|(d[19]<<24));
const flags=[];for(let i=0;i<n;i++)flags.push(d[20+i]);
// Global: initial_virtual_token 1073e12, total_supply 1e15, buyback_bps 5000; fee_config: protocol 95, creator 30
const IVT=1073000000000000n, SUPPLY=1000000000000000n, PROTO=95n, CREATOR=30n, BUYBACK=5000n;
const ceil=(a,b)=>(a+b-1n)/b;
const tok={};const add=(i,v)=>{tok[i]=(tok[i]||0n)+v;};
let inMint=A[5].token.mint; let from=5;
for(let h=0;h<n;h++){
const b=13+5*h, baseMint=A[b].pubkey, bv=A[b+3], qv=A[b+4];
const baseBal=bv.token.amount+(tok[b+3]||0n), quoteBal=qv.token.amount+(tok[b+4]||0n);
const vtok=baseBal+IVT-SUPPLY;
const sold=SUPPLY-baseBal;
const vq= sold>0n ? quoteBal*IVT/sold : 0n;
const fl=BigInt(flags[h]);
const last=(h==n-1);
if(inMint===bv.token.mint){ // sell base -> quote
const gross=vq*amount/(vtok+amount);
const pf=(fl&1n)?ceil(gross*PROTO,10000n):0n;
const cf=(fl&2n)?ceil(gross*CREATOR,10000n):0n;
const net=gross-pf-cf;
const bb=pf*BUYBACK/10000n;
add(from,-amount); add(b+3,amount);
add(b+4,-(net+bb)); if(bb>0n) add(7,bb);
amount=net; inMint=qv.token.mint; from=b+4;
} else { // buy quote -> base
const tb=((fl&1n)?PROTO:0n)+((fl&2n)?CREATOR:0n);
let x=amount*10000n/(10000n+tb);
let pf=(fl&1n)?ceil(x*PROTO,10000n):0n, cf=(fl&2n)?ceil(x*CREATOR,10000n):0n;
if(x+pf+cf>amount){ x-= (x+pf+cf-amount); }
const bb=pf*BUYBACK/10000n;
const out=vtok*(x-1n)/(vq+x-1n);
add(from,-(x+pf+cf)); add(b+4,x+pf+cf-bb); if(bb>0n) add(7,bb);
add(b+3,-out);
amount=out; inMint=bv.token.mint; from=b+3;
}
if(last){ add(from,-0n); }
}
// final output goes to destination instead of staying in the last vault
add(from,-amount); add(6,amount);
// undo: the last hop already debited output from vault? (sell: we debited net from vault, buy: debited out) -> remove double count
add(from,amount);
return {lamports:{},tokens:tok};
}

Accounts

Not published. The accounts and arguments were recovered from the bytecode; see the reconstruction.

program 6EF8rr…BEwF6P

multi_hop_curve_swap · pump.fun | debpf