Instruction
multi_hop_curve_swap
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.
24670mov64 r8, r524671mov64 r7, r424672mov64 r9, r324673stxdw [r10-400], r224674mov64 r6, r124675lddw r1, 0x1ae430 ; "Instruction: MultiHopCurveSwap"24677mov64 r2, 0x1e24678call sol_log_24679ldxdw r1, [r8-4088]2467ajlt r1, 0x8, +135 ; -> 247022467bldxdw r2, [r8-4096]2467cadd64 r1, -0x82467dldxdw r3, [r2+0]2467estxdw [r10-408], r32467fstxdw [r10-328], r124680add64 r2, 0x824681stxdw [r10-336], r224682mov64 r1, r1024683add64 r1, -0xc024684mov64 r2, r1024685add64 r2, -0x15024686call fn_0238724687ldxdw r8, [r10-184]24688ldxdw r2, [r10-192]24689lddw r1, 0x80000000000000002468bjeq r2, r1, +122 ; -> 247062468cstxdw [r10-424], r22468dstxdw [r10-416], r62468eldxdw r1, [r10-176]2468fstxdw [r10-432], r124690stb [r10-353], 0xff24691sth [r10-355], 0xffff24692stxdw [r10-344], r724693stxdw [r10-352], r924694mov64 r1, r1024695add64 r1, -0x16324696stxdw [r10-4088], r124697mov64 r1, r1024698add64 r1, -0xc024699mov64 r3, r102469aadd64 r3, -0x1602469bmov64 r5, r102469cldxdw r2, [r10-400]2469dcall fn_1aaa22469eldxdw r9, [r10-184]2469fldxdw r7, [r10-192]246a0ldxb r6, [r10-54]246a1jne r6, 0x2, +1 ; -> 246a3246a2ja +118 ; -> 24719246a3mov64 r1, r10246a4add64 r1, -0x140246a5mov64 r2, r10246a6add64 r2, -0xb0246a7mov64 r3, 0x7a246a8call fn_35367246a9ldxw r1, [r10-53]246aastxw [r10-197], r1246abldxb r1, [r10-49]246acstxb [r10-193], r1246adstxb [r10-198], r6246aestxdw [r10-328], r9246afstxdw [r10-336], r7246b0ldxh r1, [r10-355]246b1stxh [r10-160], r1246b2ldxb r1, [r10-353]246b3stxb [r10-158], r1246b4ldxdw r1, [r10-344]246b5stxdw [r10-168], r1246b6ldxdw r1, [r10-352]246b7stxdw [r10-176], r1246b8mov64 r1, r10246b9add64 r1, -0x150246bastxdw [r10-184], r1246bbldxdw r6, [r10-400]246bcstxdw [r10-192], r6246bdldxdw r1, [r10-432]246bestxdw [r10-8], r1246bfstxdw [r10-16], r8246c0ldxdw r1, [r10-424]246c1stxdw [r10-24], r1246c2mov64 r1, r10246c3add64 r1, -0x30246c4mov64 r2, r10246c5add64 r2, -0xc0246c6mov64 r4, r10246c7add64 r4, -0x18246c8ldxdw r3, [r10-408]246c9call fn_0a8d6246caldxdw r7, [r10-40]246cbldxdw r1, [r10-48]246ccmov64 r8, r6246cdjne r1, 0x0, +77 ; -> 2471b246celddw r1, 0x300000000246d0ldxdw r2, [r1+0]246d1mov64 r1, r2246d2add64 r1, -0x100246d3mov64 r3, 0x0246d4mov64 r4, 0x1246d5jgt r1, r2, +1 ; -> 246d7246d6mov64 r4, 0x0246d7ldxdw r6, [r10-416]246d8jne r4, 0x0, +1 ; -> 246da246d9mov64 r3, r1246dalddw r1, 0x300007f00246dcjeq r2, 0x0, +1 ; -> 246de246ddmov64 r1, r3246delddw r2, 0x300000008246e0jlt r1, r2, +74 ; -> 2472b246e1ldxdw r2, [r10-32]246e2lddw r3, 0x300000000246e4stxdw [r3+0], r1246e5stxdw [r1+8], r2246e6stxdw [r1+0], r7246e7mov64 r2, 0x10246e8call fn_32db9246e9mov64 r1, r10246eaadd64 r1, -0x178246ebmov64 r2, r10246ecadd64 r2, -0x150246edmov64 r3, r8246eecall fn_1adf7246efldxdw r1, [r10-224]246f0ldxdw r2, [r10-232]246f1ldxdw r3, [r2+0]246f2add64 r3, -0x1246f3ldxdw r9, [r10-368]246f4ldxdw r7, [r10-376]246f5stxdw [r2+0], r3246f6jne r3, 0x0, +3 ; -> 246fa246f7ldxdw r3, [r2+8]246f8add64 r3, -0x1246f9stxdw [r2+8], r3246faldxdw r2, [r1+0]246fbadd64 r2, -0x1246fcstxdw [r1+0], r2246fdjne r2, 0x0, +24 ; -> 24716246feldxdw r2, [r1+8]246ffadd64 r2, -0x124700stxdw [r1+8], r224701ja +20 ; -> 2471624702lddw r1, 0x1b3c6024704call fn_33e7124705mov64 r8, r024706mov64 r1, r824707and64 r1, 0x324708mov64 r2, r124709add64 r2, -0x22470ajlt r2, 0x2, +5 ; -> 247102470bjeq r1, 0x0, +4 ; -> 247102470cldxdw r1, [r8-1]2470dldxdw r2, [r8+7]2470eldxdw r2, [r2+0]2470fcallx r224710mov64 r1, r1024711add64 r1, -0x18824712mov64 r2, 0x6624713call fn_32b0924714ldxdw r9, [r10-384]24715ldxdw r7, [r10-392]24716stxdw [r6+8], r924717stxdw [r6+0], r724718exit24719ldxdw r6, [r10-416]2471aja -5 ; -> 247162471bldxdw r1, [r10-224]2471cldxdw r9, [r10-32]2471dldxdw r2, [r10-232]2471eldxdw r3, [r2+0]2471fadd64 r3, -0x124720stxdw [r2+0], r324721jne r3, 0x0, +3 ; -> 2472524722ldxdw r3, [r2+8]24723add64 r3, -0x124724stxdw [r2+8], r324725ldxdw r2, [r1+0]24726add64 r2, -0x124727stxdw [r1+0], r224728ldxdw r6, [r10-416]24729jne r2, 0x0, -20 ; -> 247162472aja -45 ; -> 246fe2472bmov64 r1, 0x12472cmov64 r2, 0x1002472dcall fn_342d8
// 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 a8d8let rem = ctx.remaining_accounts;require!(rem.len() >= 5 && rem.len() % 5 == 0, 6027); // pc a8dalet hops = rem.len() / 5;require!(fee_flags.len() == hops, 6106); // pc a8e2require!(fee_flags.iter().all(|f| *f < 4), 6106); // pc a8eblet 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 b442let 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 b267let curve = &mut hop.bonding_curve; // v3 curve: virtual_token @+64, virtual_quote @+72let mcap = curve_market_cap(curve); // used to pick fee tierlet (p_bps, c_bps) = select_fee_bps(fee_config, quote_mint, mcap); // fn_109ae: 95 / 30 todayif in_mint == hop.base_mint { // SELL base -> quotelet gross = (curve.vq as u128 * amount as u128 / (curve.vt + amount) as u128) as u64; // fn_1341e, floorlet 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_0f8d7curve.vt += amount; curve.vq -= gross; curve.real_* updated; // fn_0b027 / fn_135f3transfer_checked(input_holder -> curve_base_vault, amount); // first hop: source signed by source_authoritylet 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 -> destinationamount = net; in_mint = hop.quote_mint;} else { // BUY quote -> baselet (x, pf, cf) = v3_buy_exact_quote_in_split(amount, p_bps*protocol_on, c_bps*creator_on); // fn_0f91flet 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 a9e1set_return_data((amount, total)); // NO min-out / slippage check hereOk(())}
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 30const 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 -> quoteconst 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 -> baseconst 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 vaultadd(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 countadd(from,amount);return {lamports:{},tokens:tok};}
Accounts
Not published. The accounts and arguments were recovered from the bytecode; see the reconstruction.
program 6EF8rr…BEwF6P