outsmart 2.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +568 -0
- package/dist/cli.d.ts +44 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1251 -0
- package/dist/cli.js.map +1 -0
- package/dist/dex/byreal-clmm.d.ts +16 -0
- package/dist/dex/byreal-clmm.d.ts.map +1 -0
- package/dist/dex/byreal-clmm.js +39 -0
- package/dist/dex/byreal-clmm.js.map +1 -0
- package/dist/dex/dflow.d.ts +27 -0
- package/dist/dex/dflow.d.ts.map +1 -0
- package/dist/dex/dflow.js +200 -0
- package/dist/dex/dflow.js.map +1 -0
- package/dist/dex/fusion-amm.d.ts +44 -0
- package/dist/dex/fusion-amm.d.ts.map +1 -0
- package/dist/dex/fusion-amm.js +546 -0
- package/dist/dex/fusion-amm.js.map +1 -0
- package/dist/dex/futarchy-amm.d.ts +32 -0
- package/dist/dex/futarchy-amm.d.ts.map +1 -0
- package/dist/dex/futarchy-amm.js +443 -0
- package/dist/dex/futarchy-amm.js.map +1 -0
- package/dist/dex/futarchy-idl.d.ts +2568 -0
- package/dist/dex/futarchy-idl.d.ts.map +1 -0
- package/dist/dex/futarchy-idl.js +2570 -0
- package/dist/dex/futarchy-idl.js.map +1 -0
- package/dist/dex/futarchy-launchpad.d.ts +68 -0
- package/dist/dex/futarchy-launchpad.d.ts.map +1 -0
- package/dist/dex/futarchy-launchpad.js +377 -0
- package/dist/dex/futarchy-launchpad.js.map +1 -0
- package/dist/dex/index.d.ts +88 -0
- package/dist/dex/index.d.ts.map +1 -0
- package/dist/dex/index.js +159 -0
- package/dist/dex/index.js.map +1 -0
- package/dist/dex/jupiter-ultra.d.ts +27 -0
- package/dist/dex/jupiter-ultra.d.ts.map +1 -0
- package/dist/dex/jupiter-ultra.js +369 -0
- package/dist/dex/jupiter-ultra.js.map +1 -0
- package/dist/dex/meteora-damm-v1.d.ts +36 -0
- package/dist/dex/meteora-damm-v1.d.ts.map +1 -0
- package/dist/dex/meteora-damm-v1.js +314 -0
- package/dist/dex/meteora-damm-v1.js.map +1 -0
- package/dist/dex/meteora-damm-v2.d.ts +103 -0
- package/dist/dex/meteora-damm-v2.d.ts.map +1 -0
- package/dist/dex/meteora-damm-v2.js +1146 -0
- package/dist/dex/meteora-damm-v2.js.map +1 -0
- package/dist/dex/meteora-dbc.d.ts +38 -0
- package/dist/dex/meteora-dbc.d.ts.map +1 -0
- package/dist/dex/meteora-dbc.js +374 -0
- package/dist/dex/meteora-dbc.js.map +1 -0
- package/dist/dex/meteora-dlmm.d.ts +79 -0
- package/dist/dex/meteora-dlmm.d.ts.map +1 -0
- package/dist/dex/meteora-dlmm.js +735 -0
- package/dist/dex/meteora-dlmm.js.map +1 -0
- package/dist/dex/orca.d.ts +31 -0
- package/dist/dex/orca.d.ts.map +1 -0
- package/dist/dex/orca.js +536 -0
- package/dist/dex/orca.js.map +1 -0
- package/dist/dex/pancakeswap-clmm.d.ts +16 -0
- package/dist/dex/pancakeswap-clmm.d.ts.map +1 -0
- package/dist/dex/pancakeswap-clmm.js +39 -0
- package/dist/dex/pancakeswap-clmm.js.map +1 -0
- package/dist/dex/pumpfun-amm.d.ts +46 -0
- package/dist/dex/pumpfun-amm.d.ts.map +1 -0
- package/dist/dex/pumpfun-amm.js +692 -0
- package/dist/dex/pumpfun-amm.js.map +1 -0
- package/dist/dex/pumpfun.d.ts +41 -0
- package/dist/dex/pumpfun.d.ts.map +1 -0
- package/dist/dex/pumpfun.js +555 -0
- package/dist/dex/pumpfun.js.map +1 -0
- package/dist/dex/raydium-amm-v4.d.ts +11 -0
- package/dist/dex/raydium-amm-v4.d.ts.map +1 -0
- package/dist/dex/raydium-amm-v4.js +649 -0
- package/dist/dex/raydium-amm-v4.js.map +1 -0
- package/dist/dex/raydium-clmm.d.ts +12 -0
- package/dist/dex/raydium-clmm.d.ts.map +1 -0
- package/dist/dex/raydium-clmm.js +675 -0
- package/dist/dex/raydium-clmm.js.map +1 -0
- package/dist/dex/raydium-cpmm.d.ts +10 -0
- package/dist/dex/raydium-cpmm.d.ts.map +1 -0
- package/dist/dex/raydium-cpmm.js +613 -0
- package/dist/dex/raydium-cpmm.js.map +1 -0
- package/dist/dex/raydium-launchlab.d.ts +12 -0
- package/dist/dex/raydium-launchlab.d.ts.map +1 -0
- package/dist/dex/raydium-launchlab.js +530 -0
- package/dist/dex/raydium-launchlab.js.map +1 -0
- package/dist/dex/shared/clmm-base.d.ts +58 -0
- package/dist/dex/shared/clmm-base.d.ts.map +1 -0
- package/dist/dex/shared/clmm-base.js +891 -0
- package/dist/dex/shared/clmm-base.js.map +1 -0
- package/dist/dex/types.d.ts +601 -0
- package/dist/dex/types.d.ts.map +1 -0
- package/dist/dex/types.js +137 -0
- package/dist/dex/types.js.map +1 -0
- package/dist/dexscreener/index.d.ts +2 -0
- package/dist/dexscreener/index.d.ts.map +1 -0
- package/dist/dexscreener/index.js +18 -0
- package/dist/dexscreener/index.js.map +1 -0
- package/dist/dexscreener/info.d.ts +22 -0
- package/dist/dexscreener/info.d.ts.map +1 -0
- package/dist/dexscreener/info.js +104 -0
- package/dist/dexscreener/info.js.map +1 -0
- package/dist/helpers/check_balance.d.ts +10 -0
- package/dist/helpers/check_balance.d.ts.map +1 -0
- package/dist/helpers/check_balance.js +34 -0
- package/dist/helpers/check_balance.js.map +1 -0
- package/dist/helpers/config.d.ts +51 -0
- package/dist/helpers/config.d.ts.map +1 -0
- package/dist/helpers/config.js +118 -0
- package/dist/helpers/config.js.map +1 -0
- package/dist/helpers/index.d.ts +8 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +29 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/logger.d.ts +27 -0
- package/dist/helpers/logger.d.ts.map +1 -0
- package/dist/helpers/logger.js +39 -0
- package/dist/helpers/logger.js.map +1 -0
- package/dist/helpers/token-2022.d.ts +32 -0
- package/dist/helpers/token-2022.d.ts.map +1 -0
- package/dist/helpers/token-2022.js +48 -0
- package/dist/helpers/token-2022.js.map +1 -0
- package/dist/helpers/unwrap_sol.d.ts +2 -0
- package/dist/helpers/unwrap_sol.d.ts.map +1 -0
- package/dist/helpers/unwrap_sol.js +67 -0
- package/dist/helpers/unwrap_sol.js.map +1 -0
- package/dist/helpers/util.d.ts +698 -0
- package/dist/helpers/util.d.ts.map +1 -0
- package/dist/helpers/util.js +181 -0
- package/dist/helpers/util.js.map +1 -0
- package/dist/helpers/utils.d.ts +10 -0
- package/dist/helpers/utils.d.ts.map +1 -0
- package/dist/helpers/utils.js +97 -0
- package/dist/helpers/utils.js.map +1 -0
- package/dist/helpers/wrap_sol.d.ts +3 -0
- package/dist/helpers/wrap_sol.d.ts.map +1 -0
- package/dist/helpers/wrap_sol.js +88 -0
- package/dist/helpers/wrap_sol.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/transactions/bloXroute_tips_tx_executor.d.ts +4 -0
- package/dist/transactions/bloXroute_tips_tx_executor.d.ts.map +1 -0
- package/dist/transactions/bloXroute_tips_tx_executor.js +70 -0
- package/dist/transactions/bloXroute_tips_tx_executor.js.map +1 -0
- package/dist/transactions/index.d.ts +6 -0
- package/dist/transactions/index.d.ts.map +1 -0
- package/dist/transactions/index.js +30 -0
- package/dist/transactions/index.js.map +1 -0
- package/dist/transactions/jito_tips_tx_executor.d.ts +15 -0
- package/dist/transactions/jito_tips_tx_executor.d.ts.map +1 -0
- package/dist/transactions/jito_tips_tx_executor.js +99 -0
- package/dist/transactions/jito_tips_tx_executor.js.map +1 -0
- package/dist/transactions/landing/index.d.ts +30 -0
- package/dist/transactions/landing/index.d.ts.map +1 -0
- package/dist/transactions/landing/index.js +60 -0
- package/dist/transactions/landing/index.js.map +1 -0
- package/dist/transactions/landing/nonce-manager.d.ts +116 -0
- package/dist/transactions/landing/nonce-manager.d.ts.map +1 -0
- package/dist/transactions/landing/nonce-manager.js +393 -0
- package/dist/transactions/landing/nonce-manager.js.map +1 -0
- package/dist/transactions/landing/orchestrator.d.ts +104 -0
- package/dist/transactions/landing/orchestrator.d.ts.map +1 -0
- package/dist/transactions/landing/orchestrator.js +329 -0
- package/dist/transactions/landing/orchestrator.js.map +1 -0
- package/dist/transactions/landing/providers/astralane.d.ts +12 -0
- package/dist/transactions/landing/providers/astralane.d.ts.map +1 -0
- package/dist/transactions/landing/providers/astralane.js +132 -0
- package/dist/transactions/landing/providers/astralane.js.map +1 -0
- package/dist/transactions/landing/providers/blockrazor.d.ts +11 -0
- package/dist/transactions/landing/providers/blockrazor.d.ts.map +1 -0
- package/dist/transactions/landing/providers/blockrazor.js +134 -0
- package/dist/transactions/landing/providers/blockrazor.js.map +1 -0
- package/dist/transactions/landing/providers/bloxroute.d.ts +12 -0
- package/dist/transactions/landing/providers/bloxroute.d.ts.map +1 -0
- package/dist/transactions/landing/providers/bloxroute.js +102 -0
- package/dist/transactions/landing/providers/bloxroute.js.map +1 -0
- package/dist/transactions/landing/providers/flashblock.d.ts +10 -0
- package/dist/transactions/landing/providers/flashblock.d.ts.map +1 -0
- package/dist/transactions/landing/providers/flashblock.js +102 -0
- package/dist/transactions/landing/providers/flashblock.js.map +1 -0
- package/dist/transactions/landing/providers/helius-sender.d.ts +11 -0
- package/dist/transactions/landing/providers/helius-sender.d.ts.map +1 -0
- package/dist/transactions/landing/providers/helius-sender.js +101 -0
- package/dist/transactions/landing/providers/helius-sender.js.map +1 -0
- package/dist/transactions/landing/providers/jito.d.ts +16 -0
- package/dist/transactions/landing/providers/jito.d.ts.map +1 -0
- package/dist/transactions/landing/providers/jito.js +110 -0
- package/dist/transactions/landing/providers/jito.js.map +1 -0
- package/dist/transactions/landing/providers/nextblock.d.ts +11 -0
- package/dist/transactions/landing/providers/nextblock.d.ts.map +1 -0
- package/dist/transactions/landing/providers/nextblock.js +109 -0
- package/dist/transactions/landing/providers/nextblock.js.map +1 -0
- package/dist/transactions/landing/providers/node1.d.ts +11 -0
- package/dist/transactions/landing/providers/node1.d.ts.map +1 -0
- package/dist/transactions/landing/providers/node1.js +101 -0
- package/dist/transactions/landing/providers/node1.js.map +1 -0
- package/dist/transactions/landing/providers/nozomi.d.ts +11 -0
- package/dist/transactions/landing/providers/nozomi.d.ts.map +1 -0
- package/dist/transactions/landing/providers/nozomi.js +124 -0
- package/dist/transactions/landing/providers/nozomi.js.map +1 -0
- package/dist/transactions/landing/providers/soyas.d.ts +16 -0
- package/dist/transactions/landing/providers/soyas.d.ts.map +1 -0
- package/dist/transactions/landing/providers/soyas.js +192 -0
- package/dist/transactions/landing/providers/soyas.js.map +1 -0
- package/dist/transactions/landing/providers/stellium.d.ts +11 -0
- package/dist/transactions/landing/providers/stellium.d.ts.map +1 -0
- package/dist/transactions/landing/providers/stellium.js +102 -0
- package/dist/transactions/landing/providers/stellium.js.map +1 -0
- package/dist/transactions/landing/providers/zero-slot.d.ts +10 -0
- package/dist/transactions/landing/providers/zero-slot.d.ts.map +1 -0
- package/dist/transactions/landing/providers/zero-slot.js +92 -0
- package/dist/transactions/landing/providers/zero-slot.js.map +1 -0
- package/dist/transactions/landing/tip-accounts.d.ts +22 -0
- package/dist/transactions/landing/tip-accounts.d.ts.map +1 -0
- package/dist/transactions/landing/tip-accounts.js +140 -0
- package/dist/transactions/landing/tip-accounts.js.map +1 -0
- package/dist/transactions/landing/types.d.ts +98 -0
- package/dist/transactions/landing/types.d.ts.map +1 -0
- package/dist/transactions/landing/types.js +30 -0
- package/dist/transactions/landing/types.js.map +1 -0
- package/dist/transactions/nozomi/tx-submission.d.ts +14 -0
- package/dist/transactions/nozomi/tx-submission.d.ts.map +1 -0
- package/dist/transactions/nozomi/tx-submission.js +107 -0
- package/dist/transactions/nozomi/tx-submission.js.map +1 -0
- package/dist/transactions/send-rpc.d.ts +54 -0
- package/dist/transactions/send-rpc.d.ts.map +1 -0
- package/dist/transactions/send-rpc.js +126 -0
- package/dist/transactions/send-rpc.js.map +1 -0
- package/dist/transactions/simple_tx_executor.d.ts +10 -0
- package/dist/transactions/simple_tx_executor.d.ts.map +1 -0
- package/dist/transactions/simple_tx_executor.js +33 -0
- package/dist/transactions/simple_tx_executor.js.map +1 -0
- package/package.json +112 -0
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Raydium AMM V4 DEX Adapter
|
|
4
|
+
*
|
|
5
|
+
* Implements IDexAdapter for Raydium's classic AMM V4 program (675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8).
|
|
6
|
+
* Supports buy, snipe, findPool, getPrice, and buildSwapIxs.
|
|
7
|
+
* No sell — the source module has no sell implementation.
|
|
8
|
+
*
|
|
9
|
+
* Ported from: 100x-algo-bots/trading-modules/raydium-amm-v4/
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
46
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
47
|
+
const types_1 = require("./types");
|
|
48
|
+
const index_1 = require("./index");
|
|
49
|
+
const config_1 = require("../helpers/config");
|
|
50
|
+
const landing_1 = require("../transactions/landing");
|
|
51
|
+
const send_rpc_1 = require("../transactions/send-rpc");
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Program constants
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
const RAYDIUM_AMM_V4_PROGRAM_ID = new web3_js_1.PublicKey("675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8");
|
|
56
|
+
const SERUM_PROGRAM_ID = new web3_js_1.PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin");
|
|
57
|
+
const TOKEN_PROGRAM_ID_PK = new web3_js_1.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
58
|
+
const WSOL_MINT_PK = new web3_js_1.PublicKey(types_1.WSOL_MINT);
|
|
59
|
+
const USDC_MINT_PK = new web3_js_1.PublicKey(types_1.USDC_MINT);
|
|
60
|
+
const USDT_MINT_PK = new web3_js_1.PublicKey("Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB");
|
|
61
|
+
const USD1_MINT_PK = new web3_js_1.PublicKey("USD1ttGY1N17NEEHLmELoaybftRBUSErhqYiQzvEmuB");
|
|
62
|
+
// Swap instruction discriminator (from Rust: data.push(9))
|
|
63
|
+
const SWAP_DISCRIMINATOR = 9;
|
|
64
|
+
function decodeAmmInfo(data) {
|
|
65
|
+
if (data.length < 752) {
|
|
66
|
+
throw new Error(`AmmInfo data too short: ${data.length} bytes, expected at least 752`);
|
|
67
|
+
}
|
|
68
|
+
const nonceValue = data.readBigUInt64LE(8);
|
|
69
|
+
const nonce = Number(nonceValue & BigInt(0xff));
|
|
70
|
+
return {
|
|
71
|
+
nonce,
|
|
72
|
+
coinVault: new web3_js_1.PublicKey(data.slice(336, 368)),
|
|
73
|
+
pcVault: new web3_js_1.PublicKey(data.slice(368, 400)),
|
|
74
|
+
coinVaultMint: new web3_js_1.PublicKey(data.slice(400, 432)),
|
|
75
|
+
pcVaultMint: new web3_js_1.PublicKey(data.slice(432, 464)),
|
|
76
|
+
openOrders: new web3_js_1.PublicKey(data.slice(496, 528)),
|
|
77
|
+
market: new web3_js_1.PublicKey(data.slice(528, 560)),
|
|
78
|
+
marketProgram: new web3_js_1.PublicKey(data.slice(560, 592)),
|
|
79
|
+
targetOrders: new web3_js_1.PublicKey(data.slice(592, 624)),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async function fetchAmmInfo(connection, ammId) {
|
|
83
|
+
const accountInfo = await connection.getAccountInfo(ammId);
|
|
84
|
+
if (!accountInfo)
|
|
85
|
+
throw new Error(`AmmInfo account not found: ${ammId.toBase58()}`);
|
|
86
|
+
if (!accountInfo.owner.equals(RAYDIUM_AMM_V4_PROGRAM_ID)) {
|
|
87
|
+
throw new Error(`Invalid account owner for AmmInfo: ${accountInfo.owner.toBase58()}`);
|
|
88
|
+
}
|
|
89
|
+
return decodeAmmInfo(accountInfo.data);
|
|
90
|
+
}
|
|
91
|
+
function decodeMarketInfo(data) {
|
|
92
|
+
if (data.length < 388) {
|
|
93
|
+
throw new Error(`Market data too short: ${data.length} bytes`);
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
bids: new web3_js_1.PublicKey(data.slice(44, 76)),
|
|
97
|
+
asks: new web3_js_1.PublicKey(data.slice(76, 108)),
|
|
98
|
+
eventQueue: new web3_js_1.PublicKey(data.slice(108, 140)),
|
|
99
|
+
coinVault: new web3_js_1.PublicKey(data.slice(140, 172)),
|
|
100
|
+
pcVault: new web3_js_1.PublicKey(data.slice(172, 204)),
|
|
101
|
+
vaultSignerNonce: data.readBigUInt64LE(204),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async function fetchMarketInfo(connection, marketId) {
|
|
105
|
+
const accountInfo = await connection.getAccountInfo(marketId);
|
|
106
|
+
if (!accountInfo)
|
|
107
|
+
throw new Error(`Market account not found: ${marketId.toBase58()}`);
|
|
108
|
+
return decodeMarketInfo(accountInfo.data);
|
|
109
|
+
}
|
|
110
|
+
function deriveMarketVaultSigner(marketId, marketProgramId) {
|
|
111
|
+
// Try nonces 0-254
|
|
112
|
+
for (let nonce = 0; nonce < 255; nonce++) {
|
|
113
|
+
try {
|
|
114
|
+
const vaultSigner = web3_js_1.PublicKey.createProgramAddressSync([marketId.toBuffer(), Buffer.from(new Uint8Array(new BigUint64Array([BigInt(nonce)]).buffer))], marketProgramId);
|
|
115
|
+
return vaultSigner;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
throw new Error("Failed to derive market vault signer");
|
|
122
|
+
}
|
|
123
|
+
async function derivePoolAccounts(connection, ammId) {
|
|
124
|
+
const ammInfo = await fetchAmmInfo(connection, ammId);
|
|
125
|
+
// Derive ammAuthority
|
|
126
|
+
const AUTHORITY_AMM_SEED = Buffer.from("amm authority");
|
|
127
|
+
let ammAuthority;
|
|
128
|
+
try {
|
|
129
|
+
ammAuthority = web3_js_1.PublicKey.createProgramAddressSync([AUTHORITY_AMM_SEED, Buffer.from([ammInfo.nonce])], RAYDIUM_AMM_V4_PROGRAM_ID);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
const [authority] = web3_js_1.PublicKey.findProgramAddressSync([AUTHORITY_AMM_SEED], RAYDIUM_AMM_V4_PROGRAM_ID);
|
|
133
|
+
ammAuthority = authority;
|
|
134
|
+
}
|
|
135
|
+
// Fetch market info
|
|
136
|
+
const marketInfo = await fetchMarketInfo(connection, ammInfo.market);
|
|
137
|
+
const serumVaultSigner = deriveMarketVaultSigner(ammInfo.market, ammInfo.marketProgram);
|
|
138
|
+
// Detect token programs
|
|
139
|
+
const coinTokenProgram = await getTokenProgramForMint(connection, ammInfo.coinVaultMint);
|
|
140
|
+
const pcTokenProgram = await getTokenProgramForMint(connection, ammInfo.pcVaultMint);
|
|
141
|
+
return {
|
|
142
|
+
ammId,
|
|
143
|
+
ammAuthority,
|
|
144
|
+
ammOpenOrders: ammInfo.openOrders,
|
|
145
|
+
ammTargetOrders: ammInfo.targetOrders,
|
|
146
|
+
poolCoinTokenAccount: ammInfo.coinVault,
|
|
147
|
+
poolPcTokenAccount: ammInfo.pcVault,
|
|
148
|
+
serumMarket: ammInfo.market,
|
|
149
|
+
serumBids: marketInfo.bids,
|
|
150
|
+
serumAsks: marketInfo.asks,
|
|
151
|
+
serumEventQueue: marketInfo.eventQueue,
|
|
152
|
+
serumCoinVaultAccount: marketInfo.coinVault,
|
|
153
|
+
serumPcVaultAccount: marketInfo.pcVault,
|
|
154
|
+
serumVaultSigner,
|
|
155
|
+
coinMint: ammInfo.coinVaultMint,
|
|
156
|
+
pcMint: ammInfo.pcVaultMint,
|
|
157
|
+
coinTokenProgram,
|
|
158
|
+
pcTokenProgram,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Helpers
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
async function getTokenProgramForMint(connection, mint) {
|
|
165
|
+
const mintInfo = await connection.getAccountInfo(mint);
|
|
166
|
+
if (!mintInfo)
|
|
167
|
+
throw new Error(`Mint account not found: ${mint.toBase58()}`);
|
|
168
|
+
if (mintInfo.owner.equals(spl_token_1.TOKEN_2022_PROGRAM_ID))
|
|
169
|
+
return spl_token_1.TOKEN_2022_PROGRAM_ID;
|
|
170
|
+
return TOKEN_PROGRAM_ID_PK;
|
|
171
|
+
}
|
|
172
|
+
function isQuoteMint(mint) {
|
|
173
|
+
return (mint.equals(WSOL_MINT_PK) ||
|
|
174
|
+
mint.equals(USDC_MINT_PK) ||
|
|
175
|
+
mint.equals(USDT_MINT_PK) ||
|
|
176
|
+
mint.equals(USD1_MINT_PK));
|
|
177
|
+
}
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// SDK instruction builder (ported from raydium-amm-v4/sdk.ts)
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
function createSwapIx(poolAccounts, payer, userInputAta, userOutputAta, amountIn, minOut, inputTokenProgram) {
|
|
182
|
+
const accounts = [
|
|
183
|
+
{ pubkey: inputTokenProgram, isSigner: false, isWritable: false },
|
|
184
|
+
{ pubkey: poolAccounts.ammId, isSigner: false, isWritable: true },
|
|
185
|
+
{ pubkey: poolAccounts.ammAuthority, isSigner: false, isWritable: false },
|
|
186
|
+
{ pubkey: poolAccounts.ammOpenOrders, isSigner: false, isWritable: true },
|
|
187
|
+
{ pubkey: poolAccounts.ammTargetOrders, isSigner: false, isWritable: true },
|
|
188
|
+
{ pubkey: poolAccounts.poolCoinTokenAccount, isSigner: false, isWritable: true },
|
|
189
|
+
{ pubkey: poolAccounts.poolPcTokenAccount, isSigner: false, isWritable: true },
|
|
190
|
+
{ pubkey: SERUM_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
191
|
+
{ pubkey: poolAccounts.serumMarket, isSigner: false, isWritable: true },
|
|
192
|
+
{ pubkey: poolAccounts.serumBids, isSigner: false, isWritable: true },
|
|
193
|
+
{ pubkey: poolAccounts.serumAsks, isSigner: false, isWritable: true },
|
|
194
|
+
{ pubkey: poolAccounts.serumEventQueue, isSigner: false, isWritable: true },
|
|
195
|
+
{ pubkey: poolAccounts.serumCoinVaultAccount, isSigner: false, isWritable: true },
|
|
196
|
+
{ pubkey: poolAccounts.serumPcVaultAccount, isSigner: false, isWritable: true },
|
|
197
|
+
{ pubkey: poolAccounts.serumVaultSigner, isSigner: false, isWritable: false },
|
|
198
|
+
{ pubkey: userInputAta, isSigner: false, isWritable: true },
|
|
199
|
+
{ pubkey: userOutputAta, isSigner: false, isWritable: true },
|
|
200
|
+
{ pubkey: payer, isSigner: true, isWritable: false },
|
|
201
|
+
];
|
|
202
|
+
// Data: [9, amount_in (u64 LE), minimum_amount_out (u64 LE)] = 17 bytes
|
|
203
|
+
const data = Buffer.alloc(17);
|
|
204
|
+
data.writeUInt8(SWAP_DISCRIMINATOR, 0);
|
|
205
|
+
data.writeBigUInt64LE(amountIn, 1);
|
|
206
|
+
data.writeBigUInt64LE(minOut, 9);
|
|
207
|
+
return new web3_js_1.TransactionInstruction({
|
|
208
|
+
keys: accounts,
|
|
209
|
+
programId: RAYDIUM_AMM_V4_PROGRAM_ID,
|
|
210
|
+
data,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Pool discovery via Raydium SDK API
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
async function discoverAmmV4Pool(connection, tokenMint, quoteMint) {
|
|
217
|
+
// Try fetching via Raydium API
|
|
218
|
+
try {
|
|
219
|
+
const { Raydium } = await Promise.resolve().then(() => __importStar(require("@raydium-io/raydium-sdk-v2")));
|
|
220
|
+
const raydium = await Raydium.load({
|
|
221
|
+
connection,
|
|
222
|
+
disableLoadToken: true,
|
|
223
|
+
});
|
|
224
|
+
const listOfPools = await raydium.api.fetchPoolByMints({
|
|
225
|
+
mint1: quoteMint,
|
|
226
|
+
mint2: tokenMint,
|
|
227
|
+
});
|
|
228
|
+
for (const obj of listOfPools) {
|
|
229
|
+
if (obj.type === "Standard" && obj.programId === RAYDIUM_AMM_V4_PROGRAM_ID.toBase58()) {
|
|
230
|
+
return { poolId: obj.id };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// API unavailable, fallback below
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Adapter class
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
class RaydiumAmmV4Adapter {
|
|
243
|
+
name = "raydium-amm-v4";
|
|
244
|
+
protocol = "amm-v4";
|
|
245
|
+
capabilities = (0, types_1.defaultCapabilities)({
|
|
246
|
+
canBuy: true,
|
|
247
|
+
canSell: true,
|
|
248
|
+
canSnipe: true,
|
|
249
|
+
canFindPool: true,
|
|
250
|
+
canGetPrice: true,
|
|
251
|
+
});
|
|
252
|
+
// ----- Core: buy -----
|
|
253
|
+
async buy(params) {
|
|
254
|
+
const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
|
|
255
|
+
const connection = (0, config_1.getConnection)();
|
|
256
|
+
const wallet = (0, config_1.getWallet)();
|
|
257
|
+
const tokenMintPk = new web3_js_1.PublicKey(tokenMint);
|
|
258
|
+
const quoteMintPk = params.quoteMint ? new web3_js_1.PublicKey(params.quoteMint) : WSOL_MINT_PK;
|
|
259
|
+
const slippageBps = params.opts?.slippageBps ?? types_1.DEFAULT_SLIPPAGE_BPS;
|
|
260
|
+
const priorityFee = params.opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
|
|
261
|
+
const computeUnits = params.opts?.computeUnitLimit ?? 300_000;
|
|
262
|
+
// Resolve pool
|
|
263
|
+
let poolId;
|
|
264
|
+
if (params.poolAddress) {
|
|
265
|
+
poolId = new web3_js_1.PublicKey(params.poolAddress);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
const found = await discoverAmmV4Pool(connection, tokenMint, quoteMintPk.toBase58());
|
|
269
|
+
if (!found)
|
|
270
|
+
throw new types_1.PoolNotFoundError(this.name, tokenMint, params.quoteMint);
|
|
271
|
+
poolId = new web3_js_1.PublicKey(found.poolId);
|
|
272
|
+
}
|
|
273
|
+
// Derive all pool accounts
|
|
274
|
+
const poolAccounts = await derivePoolAccounts(connection, poolId);
|
|
275
|
+
// Detect base token program
|
|
276
|
+
const baseTokenProgram = await getTokenProgramForMint(connection, tokenMintPk);
|
|
277
|
+
// Calculate amounts
|
|
278
|
+
const quoteDecimals = quoteMintPk.equals(WSOL_MINT_PK) ? 9 : 6;
|
|
279
|
+
const amountIn = BigInt(Math.floor(params.amountSol * 10 ** quoteDecimals));
|
|
280
|
+
// Compute minOut from reserves
|
|
281
|
+
let minOut = 0n;
|
|
282
|
+
try {
|
|
283
|
+
const [coinBal, pcBal] = await Promise.all([
|
|
284
|
+
connection.getTokenAccountBalance(poolAccounts.poolCoinTokenAccount),
|
|
285
|
+
connection.getTokenAccountBalance(poolAccounts.poolPcTokenAccount),
|
|
286
|
+
]);
|
|
287
|
+
const coinReserve = BigInt(coinBal.value.amount);
|
|
288
|
+
const pcReserve = BigInt(pcBal.value.amount);
|
|
289
|
+
// Determine direction: if quoteMint == pcMint, swap pc -> coin
|
|
290
|
+
let estOut;
|
|
291
|
+
if (poolAccounts.pcMint.equals(quoteMintPk)) {
|
|
292
|
+
// Buying coinMint with pcMint
|
|
293
|
+
const k = coinReserve * pcReserve;
|
|
294
|
+
const newPc = pcReserve + amountIn;
|
|
295
|
+
estOut = coinReserve - k / newPc;
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
// Buying pcMint with coinMint (reversed pool)
|
|
299
|
+
const k = coinReserve * pcReserve;
|
|
300
|
+
const newCoin = coinReserve + amountIn;
|
|
301
|
+
estOut = pcReserve - k / newCoin;
|
|
302
|
+
}
|
|
303
|
+
minOut = (estOut * BigInt(10000 - slippageBps)) / 10000n;
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
// zero floor
|
|
307
|
+
}
|
|
308
|
+
// Build ATAs
|
|
309
|
+
const inputAta = await (0, spl_token_1.getAssociatedTokenAddress)(quoteMintPk, wallet.publicKey);
|
|
310
|
+
const outputAta = await (0, spl_token_1.getAssociatedTokenAddress)(tokenMintPk, wallet.publicKey, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID), baseTokenProgram);
|
|
311
|
+
// Determine input token program
|
|
312
|
+
let inputTokenProgram;
|
|
313
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
314
|
+
inputTokenProgram = TOKEN_PROGRAM_ID_PK;
|
|
315
|
+
}
|
|
316
|
+
else if (poolAccounts.pcMint.equals(quoteMintPk)) {
|
|
317
|
+
inputTokenProgram = poolAccounts.pcTokenProgram;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
inputTokenProgram = poolAccounts.coinTokenProgram;
|
|
321
|
+
}
|
|
322
|
+
// Build instructions
|
|
323
|
+
const ixs = [
|
|
324
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits }),
|
|
325
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
|
|
326
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, inputAta, wallet.publicKey, quoteMintPk),
|
|
327
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, outputAta, wallet.publicKey, tokenMintPk, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID) ? spl_token_1.TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID_PK),
|
|
328
|
+
];
|
|
329
|
+
// WSOL wrapping
|
|
330
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
331
|
+
const lamports = Math.floor(params.amountSol * web3_js_1.LAMPORTS_PER_SOL);
|
|
332
|
+
ixs.push(web3_js_1.SystemProgram.transfer({ fromPubkey: wallet.publicKey, toPubkey: inputAta, lamports }), (0, spl_token_1.createSyncNativeInstruction)(inputAta, TOKEN_PROGRAM_ID_PK));
|
|
333
|
+
}
|
|
334
|
+
// Swap
|
|
335
|
+
ixs.push(createSwapIx(poolAccounts, wallet.publicKey, inputAta, outputAta, amountIn, minOut, inputTokenProgram));
|
|
336
|
+
// Close WSOL
|
|
337
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
338
|
+
ixs.push((0, spl_token_1.createCloseAccountInstruction)(inputAta, wallet.publicKey, wallet.publicKey, [], TOKEN_PROGRAM_ID_PK));
|
|
339
|
+
}
|
|
340
|
+
// Submit via RPC send+confirm
|
|
341
|
+
const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet);
|
|
342
|
+
return {
|
|
343
|
+
txSignature: result.txSignature,
|
|
344
|
+
confirmed: result.confirmed,
|
|
345
|
+
amountIn: params.amountSol,
|
|
346
|
+
amountInToken: quoteMintPk.equals(WSOL_MINT_PK) ? "SOL" : quoteMintPk.toBase58(),
|
|
347
|
+
dex: this.name,
|
|
348
|
+
poolAddress: poolId.toBase58(),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
// ----- Core: sell -----
|
|
352
|
+
async sell(params) {
|
|
353
|
+
const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
|
|
354
|
+
const connection = (0, config_1.getConnection)();
|
|
355
|
+
const wallet = (0, config_1.getWallet)();
|
|
356
|
+
const tokenMintPk = new web3_js_1.PublicKey(tokenMint);
|
|
357
|
+
const quoteMintPk = params.quoteMint ? new web3_js_1.PublicKey(params.quoteMint) : WSOL_MINT_PK;
|
|
358
|
+
const slippageBps = params.opts?.slippageBps ?? types_1.DEFAULT_SLIPPAGE_BPS;
|
|
359
|
+
const priorityFee = params.opts?.priorityFeeMicroLamports ?? types_1.DEFAULT_PRIORITY_FEE_MICRO_LAMPORTS;
|
|
360
|
+
const computeUnits = params.opts?.computeUnitLimit ?? 300_000;
|
|
361
|
+
// Resolve pool
|
|
362
|
+
let poolId;
|
|
363
|
+
if (params.poolAddress) {
|
|
364
|
+
poolId = new web3_js_1.PublicKey(params.poolAddress);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
const found = await discoverAmmV4Pool(connection, tokenMint, quoteMintPk.toBase58());
|
|
368
|
+
if (!found)
|
|
369
|
+
throw new types_1.PoolNotFoundError(this.name, tokenMint, params.quoteMint);
|
|
370
|
+
poolId = new web3_js_1.PublicKey(found.poolId);
|
|
371
|
+
}
|
|
372
|
+
// Derive all pool accounts
|
|
373
|
+
const poolAccounts = await derivePoolAccounts(connection, poolId);
|
|
374
|
+
// Detect base token program
|
|
375
|
+
const baseTokenProgram = await getTokenProgramForMint(connection, tokenMintPk);
|
|
376
|
+
// Get token balance
|
|
377
|
+
const baseAta = await (0, spl_token_1.getAssociatedTokenAddress)(tokenMintPk, wallet.publicKey, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID), baseTokenProgram);
|
|
378
|
+
const tokenAccount = await (0, spl_token_1.getAccount)(connection, baseAta, "confirmed", baseTokenProgram);
|
|
379
|
+
const balance = tokenAccount.amount;
|
|
380
|
+
// Calculate sell amount based on percentage
|
|
381
|
+
const sellAmount = BigInt(Math.floor((Number(balance) * params.percentage) / 100));
|
|
382
|
+
if (sellAmount === 0n) {
|
|
383
|
+
throw new Error(`No balance to sell for ${tokenMint}`);
|
|
384
|
+
}
|
|
385
|
+
// Compute minOut from reserves (reversed direction vs buy)
|
|
386
|
+
let minOut = 0n;
|
|
387
|
+
try {
|
|
388
|
+
const [coinBal, pcBal] = await Promise.all([
|
|
389
|
+
connection.getTokenAccountBalance(poolAccounts.poolCoinTokenAccount),
|
|
390
|
+
connection.getTokenAccountBalance(poolAccounts.poolPcTokenAccount),
|
|
391
|
+
]);
|
|
392
|
+
const coinReserve = BigInt(coinBal.value.amount);
|
|
393
|
+
const pcReserve = BigInt(pcBal.value.amount);
|
|
394
|
+
// Selling token → receiving quote. Determine direction.
|
|
395
|
+
let estOut;
|
|
396
|
+
if (poolAccounts.coinMint.equals(tokenMintPk)) {
|
|
397
|
+
// Selling coinMint → receiving pcMint
|
|
398
|
+
const k = coinReserve * pcReserve;
|
|
399
|
+
const newCoin = coinReserve + sellAmount;
|
|
400
|
+
estOut = pcReserve - k / newCoin;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
// Selling pcMint → receiving coinMint
|
|
404
|
+
const k = coinReserve * pcReserve;
|
|
405
|
+
const newPc = pcReserve + sellAmount;
|
|
406
|
+
estOut = coinReserve - k / newPc;
|
|
407
|
+
}
|
|
408
|
+
minOut = (estOut * BigInt(10000 - slippageBps)) / 10000n;
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
// zero floor
|
|
412
|
+
}
|
|
413
|
+
// Build ATAs — reversed from buy: input is token, output is quote
|
|
414
|
+
const userInputAta = baseAta;
|
|
415
|
+
const quoteTokenProgram = quoteMintPk.equals(WSOL_MINT_PK)
|
|
416
|
+
? TOKEN_PROGRAM_ID_PK
|
|
417
|
+
: await getTokenProgramForMint(connection, quoteMintPk);
|
|
418
|
+
const userOutputAta = await (0, spl_token_1.getAssociatedTokenAddress)(quoteMintPk, wallet.publicKey);
|
|
419
|
+
// Determine input token program (the token being sold)
|
|
420
|
+
const inputTokenProgram = baseTokenProgram;
|
|
421
|
+
// Build instructions
|
|
422
|
+
const ixs = [
|
|
423
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits }),
|
|
424
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
|
|
425
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, userInputAta, wallet.publicKey, tokenMintPk, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID) ? spl_token_1.TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID_PK),
|
|
426
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, userOutputAta, wallet.publicKey, quoteMintPk),
|
|
427
|
+
];
|
|
428
|
+
// WSOL output: create WSOL ATA so we can receive wrapped SOL, then close after
|
|
429
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
430
|
+
// ATA creation already handled above via idempotent instruction
|
|
431
|
+
}
|
|
432
|
+
// Swap — input is token ATA, output is quote ATA
|
|
433
|
+
ixs.push(createSwapIx(poolAccounts, wallet.publicKey, userInputAta, userOutputAta, sellAmount, minOut, inputTokenProgram));
|
|
434
|
+
// Close WSOL output ATA to unwrap back to SOL
|
|
435
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
436
|
+
ixs.push((0, spl_token_1.createCloseAccountInstruction)(userOutputAta, wallet.publicKey, wallet.publicKey, [], TOKEN_PROGRAM_ID_PK));
|
|
437
|
+
}
|
|
438
|
+
// Submit via RPC send+confirm
|
|
439
|
+
const result = await (0, send_rpc_1.sendAndConfirmVtx)(connection, ixs, wallet);
|
|
440
|
+
// Human-readable sell amount
|
|
441
|
+
let tokenDecimals = 9;
|
|
442
|
+
try {
|
|
443
|
+
const mintData = await connection.getTokenSupply(tokenMintPk);
|
|
444
|
+
tokenDecimals = mintData.value.decimals;
|
|
445
|
+
}
|
|
446
|
+
catch { /* fallback to 9 */ }
|
|
447
|
+
const humanAmount = Number(sellAmount) / Math.pow(10, tokenDecimals);
|
|
448
|
+
return {
|
|
449
|
+
txSignature: result.txSignature,
|
|
450
|
+
confirmed: result.confirmed,
|
|
451
|
+
amountIn: humanAmount,
|
|
452
|
+
amountInToken: tokenMint,
|
|
453
|
+
dex: this.name,
|
|
454
|
+
poolAddress: poolId.toBase58(),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
// ----- Snipe -----
|
|
458
|
+
async snipe(params) {
|
|
459
|
+
const connection = (0, config_1.getConnection)();
|
|
460
|
+
const wallet = (0, config_1.getWallet)();
|
|
461
|
+
const tokenMint = new web3_js_1.PublicKey(params.tokenMint);
|
|
462
|
+
const quoteMintPk = params.quoteMint ? new web3_js_1.PublicKey(params.quoteMint) : WSOL_MINT_PK;
|
|
463
|
+
const poolId = new web3_js_1.PublicKey(params.poolAddress);
|
|
464
|
+
const priorityFee = params.opts?.priorityFeeMicroLamports ?? 10_000_000;
|
|
465
|
+
// Derive all pool accounts
|
|
466
|
+
const poolAccounts = await derivePoolAccounts(connection, poolId);
|
|
467
|
+
// Detect base token program
|
|
468
|
+
const baseTokenProgram = await getTokenProgramForMint(connection, tokenMint);
|
|
469
|
+
// Calculate amounts
|
|
470
|
+
const quoteDecimals = quoteMintPk.equals(WSOL_MINT_PK) ? 9 : 6;
|
|
471
|
+
const amountIn = BigInt(Math.floor(params.amountSol * 10 ** quoteDecimals));
|
|
472
|
+
const minOut = 0n; // Unlimited slippage for sniping
|
|
473
|
+
// Build ATAs
|
|
474
|
+
const inputAta = await (0, spl_token_1.getAssociatedTokenAddress)(quoteMintPk, wallet.publicKey);
|
|
475
|
+
const outputAta = await (0, spl_token_1.getAssociatedTokenAddress)(tokenMint, wallet.publicKey, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID), baseTokenProgram);
|
|
476
|
+
// Determine input token program
|
|
477
|
+
let inputTokenProgram;
|
|
478
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
479
|
+
inputTokenProgram = TOKEN_PROGRAM_ID_PK;
|
|
480
|
+
}
|
|
481
|
+
else if (poolAccounts.pcMint.equals(quoteMintPk)) {
|
|
482
|
+
inputTokenProgram = poolAccounts.pcTokenProgram;
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
inputTokenProgram = poolAccounts.coinTokenProgram;
|
|
486
|
+
}
|
|
487
|
+
// Build instructions
|
|
488
|
+
const ixs = [
|
|
489
|
+
web3_js_1.ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityFee }),
|
|
490
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, inputAta, wallet.publicKey, quoteMintPk),
|
|
491
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, outputAta, wallet.publicKey, tokenMint, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID) ? spl_token_1.TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID_PK),
|
|
492
|
+
];
|
|
493
|
+
// WSOL wrapping
|
|
494
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
495
|
+
const lamports = Math.floor(params.amountSol * web3_js_1.LAMPORTS_PER_SOL);
|
|
496
|
+
ixs.push(web3_js_1.SystemProgram.transfer({ fromPubkey: wallet.publicKey, toPubkey: inputAta, lamports }), (0, spl_token_1.createSyncNativeInstruction)(inputAta, TOKEN_PROGRAM_ID_PK));
|
|
497
|
+
}
|
|
498
|
+
// Swap
|
|
499
|
+
ixs.push(createSwapIx(poolAccounts, wallet.publicKey, inputAta, outputAta, amountIn, minOut, inputTokenProgram));
|
|
500
|
+
// Close WSOL
|
|
501
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
502
|
+
ixs.push((0, spl_token_1.createCloseAccountInstruction)(inputAta, wallet.publicKey, wallet.publicKey, [], TOKEN_PROGRAM_ID_PK));
|
|
503
|
+
}
|
|
504
|
+
// Submit via landing layer
|
|
505
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
506
|
+
const results = await (0, landing_1.landTransaction)(ixs, wallet, blockhash, {
|
|
507
|
+
dex: this.name,
|
|
508
|
+
operation: "snipe",
|
|
509
|
+
tipSol: params.tipSol,
|
|
510
|
+
});
|
|
511
|
+
const firstAccepted = results.find((r) => r.accepted);
|
|
512
|
+
return {
|
|
513
|
+
txSignature: firstAccepted?.signature ?? "",
|
|
514
|
+
confirmed: !!firstAccepted?.accepted,
|
|
515
|
+
amountIn: params.amountSol,
|
|
516
|
+
amountInToken: quoteMintPk.equals(WSOL_MINT_PK) ? "SOL" : quoteMintPk.toBase58(),
|
|
517
|
+
dex: this.name,
|
|
518
|
+
poolAddress: poolId.toBase58(),
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
// ----- Build swap IXs -----
|
|
522
|
+
async buildSwapIxs(params) {
|
|
523
|
+
if (!("amountSol" in params)) {
|
|
524
|
+
throw new types_1.UnsupportedOperationError(this.name, "buildSwapIxs(sell)");
|
|
525
|
+
}
|
|
526
|
+
const tokenMint = (0, types_1.requireTokenMint)(params, this.name);
|
|
527
|
+
const connection = (0, config_1.getConnection)();
|
|
528
|
+
const wallet = (0, config_1.getWallet)();
|
|
529
|
+
const buyParams = params;
|
|
530
|
+
const tokenMintPk = new web3_js_1.PublicKey(tokenMint);
|
|
531
|
+
const quoteMintPk = buyParams.quoteMint ? new web3_js_1.PublicKey(buyParams.quoteMint) : WSOL_MINT_PK;
|
|
532
|
+
// Resolve pool
|
|
533
|
+
let poolId;
|
|
534
|
+
if (buyParams.poolAddress) {
|
|
535
|
+
poolId = new web3_js_1.PublicKey(buyParams.poolAddress);
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
const found = await discoverAmmV4Pool(connection, tokenMint, quoteMintPk.toBase58());
|
|
539
|
+
if (!found)
|
|
540
|
+
throw new types_1.PoolNotFoundError(this.name, tokenMint, buyParams.quoteMint);
|
|
541
|
+
poolId = new web3_js_1.PublicKey(found.poolId);
|
|
542
|
+
}
|
|
543
|
+
const poolAccounts = await derivePoolAccounts(connection, poolId);
|
|
544
|
+
const baseTokenProgram = await getTokenProgramForMint(connection, tokenMintPk);
|
|
545
|
+
const quoteDecimals = quoteMintPk.equals(WSOL_MINT_PK) ? 9 : 6;
|
|
546
|
+
const amountIn = BigInt(Math.floor(buyParams.amountSol * 10 ** quoteDecimals));
|
|
547
|
+
const inputAta = await (0, spl_token_1.getAssociatedTokenAddress)(quoteMintPk, wallet.publicKey);
|
|
548
|
+
const outputAta = await (0, spl_token_1.getAssociatedTokenAddress)(tokenMintPk, wallet.publicKey, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID), baseTokenProgram);
|
|
549
|
+
let inputTokenProgram;
|
|
550
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
551
|
+
inputTokenProgram = TOKEN_PROGRAM_ID_PK;
|
|
552
|
+
}
|
|
553
|
+
else if (poolAccounts.pcMint.equals(quoteMintPk)) {
|
|
554
|
+
inputTokenProgram = poolAccounts.pcTokenProgram;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
inputTokenProgram = poolAccounts.coinTokenProgram;
|
|
558
|
+
}
|
|
559
|
+
const instructions = [
|
|
560
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, inputAta, wallet.publicKey, quoteMintPk),
|
|
561
|
+
(0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(wallet.publicKey, outputAta, wallet.publicKey, tokenMintPk, baseTokenProgram.equals(spl_token_1.TOKEN_2022_PROGRAM_ID) ? spl_token_1.TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID_PK),
|
|
562
|
+
];
|
|
563
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
564
|
+
const lamports = Math.floor(buyParams.amountSol * web3_js_1.LAMPORTS_PER_SOL);
|
|
565
|
+
instructions.push(web3_js_1.SystemProgram.transfer({ fromPubkey: wallet.publicKey, toPubkey: inputAta, lamports }), (0, spl_token_1.createSyncNativeInstruction)(inputAta, TOKEN_PROGRAM_ID_PK));
|
|
566
|
+
}
|
|
567
|
+
instructions.push(createSwapIx(poolAccounts, wallet.publicKey, inputAta, outputAta, amountIn, 0n, inputTokenProgram));
|
|
568
|
+
if (quoteMintPk.equals(WSOL_MINT_PK)) {
|
|
569
|
+
instructions.push((0, spl_token_1.createCloseAccountInstruction)(inputAta, wallet.publicKey, wallet.publicKey, [], TOKEN_PROGRAM_ID_PK));
|
|
570
|
+
}
|
|
571
|
+
return { instructions, signers: [] };
|
|
572
|
+
}
|
|
573
|
+
// ----- Pool discovery -----
|
|
574
|
+
async findPool(baseMint, quoteMint) {
|
|
575
|
+
const connection = (0, config_1.getConnection)();
|
|
576
|
+
const quoteMintStr = quoteMint ?? types_1.WSOL_MINT;
|
|
577
|
+
const found = await discoverAmmV4Pool(connection, baseMint, quoteMintStr);
|
|
578
|
+
if (!found)
|
|
579
|
+
return null;
|
|
580
|
+
const poolId = new web3_js_1.PublicKey(found.poolId);
|
|
581
|
+
try {
|
|
582
|
+
const ammInfo = await fetchAmmInfo(connection, poolId);
|
|
583
|
+
const [coinBal, pcBal] = await Promise.all([
|
|
584
|
+
connection.getTokenAccountBalance(ammInfo.coinVault),
|
|
585
|
+
connection.getTokenAccountBalance(ammInfo.pcVault),
|
|
586
|
+
]);
|
|
587
|
+
return {
|
|
588
|
+
address: found.poolId,
|
|
589
|
+
dex: this.name,
|
|
590
|
+
protocol: this.protocol,
|
|
591
|
+
baseMint: ammInfo.coinVaultMint.toBase58(),
|
|
592
|
+
quoteMint: ammInfo.pcVaultMint.toBase58(),
|
|
593
|
+
baseDecimals: coinBal.value.decimals,
|
|
594
|
+
quoteDecimals: pcBal.value.decimals,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
return {
|
|
599
|
+
address: found.poolId,
|
|
600
|
+
dex: this.name,
|
|
601
|
+
protocol: this.protocol,
|
|
602
|
+
baseMint,
|
|
603
|
+
quoteMint: quoteMintStr,
|
|
604
|
+
baseDecimals: 6,
|
|
605
|
+
quoteDecimals: 9,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
// ----- Price -----
|
|
610
|
+
async getPrice(poolAddress) {
|
|
611
|
+
const connection = (0, config_1.getConnection)();
|
|
612
|
+
const poolId = new web3_js_1.PublicKey(poolAddress);
|
|
613
|
+
const ammInfo = await fetchAmmInfo(connection, poolId);
|
|
614
|
+
const [coinBal, pcBal] = await Promise.all([
|
|
615
|
+
connection.getTokenAccountBalance(ammInfo.coinVault),
|
|
616
|
+
connection.getTokenAccountBalance(ammInfo.pcVault),
|
|
617
|
+
]);
|
|
618
|
+
const coinReserve = Number(coinBal.value.amount) / 10 ** coinBal.value.decimals;
|
|
619
|
+
const pcReserve = Number(pcBal.value.amount) / 10 ** pcBal.value.decimals;
|
|
620
|
+
// Determine which is base vs quote
|
|
621
|
+
const pcIsQuote = isQuoteMint(ammInfo.pcVaultMint);
|
|
622
|
+
let price;
|
|
623
|
+
let baseMint;
|
|
624
|
+
let quoteMintOut;
|
|
625
|
+
if (pcIsQuote) {
|
|
626
|
+
price = coinReserve > 0 ? pcReserve / coinReserve : 0;
|
|
627
|
+
baseMint = ammInfo.coinVaultMint;
|
|
628
|
+
quoteMintOut = ammInfo.pcVaultMint;
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
price = pcReserve > 0 ? coinReserve / pcReserve : 0;
|
|
632
|
+
baseMint = ammInfo.pcVaultMint;
|
|
633
|
+
quoteMintOut = ammInfo.coinVaultMint;
|
|
634
|
+
}
|
|
635
|
+
return {
|
|
636
|
+
price,
|
|
637
|
+
baseMint: baseMint.toBase58(),
|
|
638
|
+
quoteMint: quoteMintOut.toBase58(),
|
|
639
|
+
source: "on-chain",
|
|
640
|
+
poolAddress,
|
|
641
|
+
timestamp: Date.now(),
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
// ---------------------------------------------------------------------------
|
|
646
|
+
// Register adapter
|
|
647
|
+
// ---------------------------------------------------------------------------
|
|
648
|
+
(0, index_1.registerAdapter)(new RaydiumAmmV4Adapter());
|
|
649
|
+
//# sourceMappingURL=raydium-amm-v4.js.map
|