-
Notifications
You must be signed in to change notification settings - Fork 36
Expand file tree
/
Copy path41-amm-buy.ts
More file actions
142 lines (130 loc) · 4.61 KB
/
Copy path41-amm-buy.ts
File metadata and controls
142 lines (130 loc) · 4.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
* Example 41: AMM Buy
*
* Category: AMM & Advanced
*
* Quotes a buy on a graduated token's PumpAMM pool with ammQuoteBuy, then
* builds the swap instructions with ammBuyInstructions without sending
* anything. Shows how to read a quote: effective price, fee load, and how
* far the fill sits from the pool's spot price.
*
* Run: npm run example 41
*/
import { OnlinePumpSdk, type AmmBuyQuote } from "@nirholas/pump-sdk";
import BN from "bn.js";
import { getConnection } from "./_lib/connection";
import { formatSol, formatTokens, heading, row } from "./_lib/format";
import { findGraduatedMint } from "./_lib/discovery";
import { loadWallet } from "./_lib/wallet";
/** 1 whole Pump token = 1e6 raw units (6 decimals). */
const TOKEN_UNITS = new BN(1_000_000);
/** Human-readable breakdown of an AMM buy quote. All math stays in BN. */
export interface AmmBuyQuoteBreakdown {
/** Lamports paid per whole token at this fill (input / output). */
effectivePriceLamports: BN;
/** Pool spot price in lamports per whole token (quoteReserve / baseReserve). */
spotPriceLamports: BN;
/** Total fees as basis points of the SOL spent. */
feeBpsOfInput: BN;
/** How far the effective price sits above spot, in basis points. */
premiumOverSpotBps: BN;
}
/**
* Interpret an AMM buy quote.
*
* The quote alone tells you tokens out and fees; what a trader actually
* wants to know is the effective price and how much of it is fees versus
* price impact. Both fall out of the quote with integer math only.
*/
export function interpretAmmBuyQuote(quote: AmmBuyQuote): AmmBuyQuoteBreakdown {
if (quote.tokensOut.isZero()) {
throw new Error("Quote returned zero tokens out; input too small to fill");
}
if (quote.poolBaseAmount.isZero() || quote.poolQuoteAmount.isZero()) {
throw new Error(
"Pool reserves are empty; this pool has no liquidity to price against",
);
}
const effectivePriceLamports = quote.solSpent
.mul(TOKEN_UNITS)
.div(quote.tokensOut);
const spotPriceLamports = quote.poolQuoteAmount
.mul(TOKEN_UNITS)
.div(quote.poolBaseAmount);
const feeBpsOfInput = quote.feesLamports.muln(10_000).div(quote.solSpent);
// effective/spot = (solSpent/tokensOut) / (poolQuote/poolBase); comparing
// via cross-multiplication avoids the per-token prices rounding to zero
// on micro-cap pools.
const crossEffective = quote.solSpent.mul(quote.poolBaseAmount);
const crossSpot = quote.poolQuoteAmount.mul(quote.tokensOut);
const premiumOverSpotBps = crossEffective
.sub(crossSpot)
.muln(10_000)
.div(crossSpot);
return {
effectivePriceLamports,
spotPriceLamports,
feeBpsOfInput,
premiumOverSpotBps,
};
}
export async function main(): Promise<void> {
const connection = getConnection();
const wallet = loadWallet();
const sdk = new OnlinePumpSdk(connection);
heading("Finding a graduated token");
const { mint } = await findGraduatedMint(connection);
row("Mint", mint.toBase58());
row("Buyer", wallet.publicKey.toBase58());
const solAmount = new BN(100_000_000); // 0.1 SOL
heading("Quote: ammQuoteBuy");
const quote = await sdk.ammQuoteBuy({
mint,
user: wallet.publicKey,
quoteAmountIn: solAmount,
});
row("SOL in", formatSol(quote.solSpent));
row("Tokens out", formatTokens(quote.tokensOut));
row("Fees", formatSol(quote.feesLamports, 6));
row("Pool base reserve", formatTokens(quote.poolBaseAmount));
row("Pool quote reserve", formatSol(quote.poolQuoteAmount));
heading("Quote interpretation");
const breakdown = interpretAmmBuyQuote(quote);
row("Spot price", `${breakdown.spotPriceLamports.toString()} lamports/token`);
row(
"Effective price",
`${breakdown.effectivePriceLamports.toString()} lamports/token`,
);
row("Fee load", `${breakdown.feeBpsOfInput.toString()} bps of input`);
row(
"Premium over spot",
`${breakdown.premiumOverSpotBps.toString()} bps (fees + price impact)`,
);
heading("Instructions: ammBuyInstructions (not sent)");
const ixs = await sdk.ammBuyInstructions({
mint,
user: wallet.publicKey,
solAmount,
slippageBps: 100, // 1%
});
row("Instruction count", ixs.length);
for (const [i, ix] of ixs.entries()) {
row(
` ix[${i}]`,
`${ix.programId.toBase58()} keys=${ix.keys.length} data=${ix.data.length}B`,
);
}
heading("Next step (not performed here)");
console.log(
"Compose these instructions into a transaction, sign with the wallet,",
);
console.log(
"and send. The wSOL wrapping and ATA creation are already included.",
);
}
if (require.main === module) {
main().catch((err) => {
console.error(err);
process.exit(1);
});
}