genius-intents 0.17.5 → 0.18.1-develop.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/CHANGELOG.md +79 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.config.d.ts +9 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.config.d.ts.map +1 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.config.js +14 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.config.js.map +1 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.service.d.ts +38 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.service.d.ts.map +1 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.service.js +308 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.service.js.map +1 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.types.d.ts +41 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.types.d.ts.map +1 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.types.js +3 -0
- package/dist/direct-pools/fourmeme/fourmeme-direct.types.js.map +1 -0
- package/dist/lib/dex/hyperswap/hyperswapV2-router.abi.d.ts +3 -0
- package/dist/lib/dex/hyperswap/hyperswapV2-router.abi.d.ts.map +1 -0
- package/dist/lib/dex/hyperswap/hyperswapV2-router.abi.js +249 -0
- package/dist/lib/dex/hyperswap/hyperswapV2-router.abi.js.map +1 -0
- package/dist/protocols/four-meme/four-meme.service.d.ts +23 -43
- package/dist/protocols/four-meme/four-meme.service.d.ts.map +1 -1
- package/dist/protocols/four-meme/four-meme.service.js +391 -157
- package/dist/protocols/four-meme/four-meme.service.js.map +1 -1
- package/dist/protocols/four-meme/four-meme.types.d.ts +47 -0
- package/dist/protocols/four-meme/four-meme.types.d.ts.map +1 -1
- package/dist/protocols/lifi/lifi.service.d.ts.map +1 -1
- package/dist/protocols/lifi/lifi.service.js +2 -0
- package/dist/protocols/lifi/lifi.service.js.map +1 -1
- package/dist/protocols/relay/relay.service.d.ts.map +1 -1
- package/dist/protocols/relay/relay.service.js +1 -0
- package/dist/protocols/relay/relay.service.js.map +1 -1
- package/dist/protocols/v2-dex/v2-dex.service.d.ts +1 -0
- package/dist/protocols/v2-dex/v2-dex.service.d.ts.map +1 -1
- package/dist/protocols/v2-dex/v2-dex.service.js +43 -67
- package/dist/protocols/v2-dex/v2-dex.service.js.map +1 -1
- package/dist/protocols/v2-dex/v2-dex.types.d.ts +9 -2
- package/dist/protocols/v2-dex/v2-dex.types.d.ts.map +1 -1
- package/dist/protocols/v2-dex/v2-dex.types.js +1 -0
- package/dist/protocols/v2-dex/v2-dex.types.js.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.service.d.ts +1 -0
- package/dist/protocols/v3-dex/v3-dex.service.d.ts.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.service.js +73 -16
- package/dist/protocols/v3-dex/v3-dex.service.js.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.types.d.ts +2 -1
- package/dist/protocols/v3-dex/v3-dex.types.d.ts.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.types.js +1 -0
- package/dist/protocols/v3-dex/v3-dex.types.js.map +1 -1
- package/dist/types/price-params.d.ts +4 -0
- package/dist/types/price-params.d.ts.map +1 -1
- package/dist/types/price-response.d.ts +2 -1
- package/dist/types/price-response.d.ts.map +1 -1
- package/dist/types/quote-response.d.ts +2 -1
- package/dist/types/quote-response.d.ts.map +1 -1
- package/dist/utils/check-vm.d.ts.map +1 -1
- package/dist/utils/check-vm.js +2 -1
- package/dist/utils/check-vm.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,53 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FourMemeService = void 0;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
const chains_1 = require("viem/chains");
|
|
4
6
|
const ethers_1 = require("ethers");
|
|
5
7
|
const enums_1 = require("../../types/enums");
|
|
6
8
|
const logger_1 = require("../../utils/logger");
|
|
7
9
|
const is_native_1 = require("../../utils/is-native");
|
|
8
10
|
const throw_error_1 = require("../../utils/throw-error");
|
|
9
|
-
const create_error_message_1 = require("../../utils/create-error-message");
|
|
10
11
|
const constants_1 = require("../../utils/constants");
|
|
12
|
+
const fourmeme_direct_config_1 = require("../../direct-pools/fourmeme/fourmeme-direct.config");
|
|
11
13
|
let logger;
|
|
12
14
|
/**
|
|
13
15
|
* The `FourMemeService` class implements the IIntentProtocol interface for token swaps
|
|
14
16
|
* using the four.meme TokenManager contracts. It provides functionality for fetching price quotes
|
|
15
17
|
* and generating transaction data for token swaps on BSC where either input or output must be BNB.
|
|
16
18
|
*
|
|
17
|
-
*
|
|
19
|
+
* NOTE: Per your request, legacy ABIs and legacy BNB↔BNB-paired behavior are UNCHANGED.
|
|
20
|
+
* We only add optional paths that call HelperV3.buyWithEth / HelperV3.sellForEth for ERC20-quoted pairs.
|
|
18
21
|
*/
|
|
19
22
|
class FourMemeService {
|
|
20
23
|
constructor(config) {
|
|
21
|
-
/**
|
|
22
|
-
* The protocol identifier for four.meme Protocol.
|
|
23
|
-
*/
|
|
24
24
|
this.protocol = enums_1.ProtocolEnum.FOUR_MEME;
|
|
25
|
-
/**
|
|
26
|
-
* The list of blockchain networks supported by the four.meme service.
|
|
27
|
-
*/
|
|
28
25
|
this.chains = [enums_1.ChainIdEnum.BSC];
|
|
29
|
-
/**
|
|
30
|
-
* Indicates that the service operates only on a single blockchain.
|
|
31
|
-
*/
|
|
32
26
|
this.singleChain = true;
|
|
33
|
-
/**
|
|
34
|
-
* Indicates that the service does not support cross-chain operations.
|
|
35
|
-
*/
|
|
36
27
|
this.multiChain = false;
|
|
37
28
|
/**
|
|
38
|
-
*
|
|
29
|
+
* TokenManagerHelper V3 (wrapper)
|
|
39
30
|
*/
|
|
40
31
|
this._tokenManagerHelperV3 = '0xF251F83e40a78868FcfA3FA4599Dad6494E46034';
|
|
41
|
-
/**
|
|
42
|
-
* ABI for TokenManagerHelper3 contract
|
|
43
|
-
*/
|
|
44
|
-
this._helperAbi = [
|
|
45
|
-
'function getTokenInfo(address token) external view returns (uint256 version, address tokenManager, address quote, uint256 lastPrice, uint256 tradingFeeRate, uint256 minTradingFee, uint256 launchTime, uint256 offers, uint256 maxOffers, uint256 funds, uint256 maxFunds, bool liquidityAdded)',
|
|
46
|
-
'function tryBuy(address token, uint256 amount, uint256 funds) external view returns (address tokenManager, address quote, uint256 estimatedAmount, uint256 estimatedCost, uint256 estimatedFee, uint256 amountMsgValue, uint256 amountApproval, uint256 amountFunds)',
|
|
47
|
-
'function trySell(address token, uint256 amount) external view returns (address tokenManager, address quote, uint256 funds, uint256 fee)',
|
|
48
|
-
];
|
|
49
32
|
/**
|
|
50
33
|
* ABI for TokenManager V1 contract
|
|
34
|
+
* (UNCHANGED)
|
|
51
35
|
*/
|
|
52
36
|
this._tokenManagerV1Abi = [
|
|
53
37
|
'function purchaseTokenAMAP(address token, uint256 funds, uint256 minAmount) external payable',
|
|
@@ -56,14 +40,89 @@ class FourMemeService {
|
|
|
56
40
|
];
|
|
57
41
|
/**
|
|
58
42
|
* ABI for TokenManager V2 contract
|
|
43
|
+
* (UNCHANGED, even if signatures look odd — preserving your legacy logic)
|
|
59
44
|
*/
|
|
60
45
|
this._tokenManagerV2Abi = [
|
|
61
46
|
'function buyTokenAMAP(uint256 amountOut, address token, uint256 funds, uint256 minAmount) external payable',
|
|
62
47
|
'function sellToken(uint256 amountOut, address token, uint256 expectedAmount, uint256 minAmount) external',
|
|
63
48
|
];
|
|
49
|
+
/**
|
|
50
|
+
* Secondary ABI for TokenManager V2 (UNCHANGED)
|
|
51
|
+
*/
|
|
64
52
|
this._tokenManagerV2SecondaryAbi = [
|
|
65
53
|
'function buyTokenAMAP(address token, uint256 funds, uint256 minAmount) external payable',
|
|
66
54
|
];
|
|
55
|
+
/**
|
|
56
|
+
* Helper V3 read ABI (UNCHANGED)
|
|
57
|
+
*/
|
|
58
|
+
this._helperViemAbi = [
|
|
59
|
+
{
|
|
60
|
+
type: 'function',
|
|
61
|
+
name: 'getTokenInfo',
|
|
62
|
+
stateMutability: 'view',
|
|
63
|
+
inputs: [{ name: 'token', type: 'address' }],
|
|
64
|
+
outputs: [
|
|
65
|
+
{ name: 'version', type: 'uint256' },
|
|
66
|
+
{ name: 'tokenManager', type: 'address' },
|
|
67
|
+
{ name: 'quote', type: 'address' },
|
|
68
|
+
{ name: 'lastPrice', type: 'uint256' },
|
|
69
|
+
{ name: 'tradingFeeRate', type: 'uint256' },
|
|
70
|
+
{ name: 'minTradingFee', type: 'uint256' },
|
|
71
|
+
{ name: 'launchTime', type: 'uint256' },
|
|
72
|
+
{ name: 'offers', type: 'uint256' },
|
|
73
|
+
{ name: 'maxOffers', type: 'uint256' },
|
|
74
|
+
{ name: 'funds', type: 'uint256' },
|
|
75
|
+
{ name: 'maxFunds', type: 'uint256' },
|
|
76
|
+
{ name: 'liquidityAdded', type: 'bool' },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'function',
|
|
81
|
+
name: 'tryBuy',
|
|
82
|
+
stateMutability: 'view',
|
|
83
|
+
inputs: [
|
|
84
|
+
{ name: 'token', type: 'address' },
|
|
85
|
+
{ name: 'amount', type: 'uint256' }, // ignored per your usage, use 0
|
|
86
|
+
{ name: 'funds', type: 'uint256' }, // BNB in
|
|
87
|
+
],
|
|
88
|
+
outputs: [
|
|
89
|
+
{ name: 'tokenManager', type: 'address' },
|
|
90
|
+
{ name: 'quote', type: 'address' },
|
|
91
|
+
{ name: 'estimatedAmount', type: 'uint256' },
|
|
92
|
+
{ name: 'estimatedCost', type: 'uint256' },
|
|
93
|
+
{ name: 'estimatedFee', type: 'uint256' },
|
|
94
|
+
{ name: 'amountMsgValue', type: 'uint256' },
|
|
95
|
+
{ name: 'amountApproval', type: 'uint256' },
|
|
96
|
+
{ name: 'amountFunds', type: 'uint256' },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: 'function',
|
|
101
|
+
name: 'trySell',
|
|
102
|
+
stateMutability: 'view',
|
|
103
|
+
inputs: [
|
|
104
|
+
{ name: 'token', type: 'address' },
|
|
105
|
+
{ name: 'amount', type: 'uint256' }, // token amount in
|
|
106
|
+
],
|
|
107
|
+
outputs: [
|
|
108
|
+
{ name: 'tokenManager', type: 'address' },
|
|
109
|
+
{ name: 'quote', type: 'address' },
|
|
110
|
+
{ name: 'funds', type: 'uint256' }, // BNB out (for BNB-quoted), or quote-ERC20 amount (for ERC20-quoted)
|
|
111
|
+
{ name: 'fee', type: 'uint256' },
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
/**
|
|
116
|
+
* NEW: Minimal Helper V3 tx ABI for the new entrypoints.
|
|
117
|
+
* We keep it separate so original ABIs & logic remain untouched.
|
|
118
|
+
*/
|
|
119
|
+
this._helperV3EthAbi = [
|
|
120
|
+
// buy ERC20-quoted token using BNB directly
|
|
121
|
+
'function buyWithEth(uint256 origin, address token, address to, uint256 funds, uint256 minAmount) payable',
|
|
122
|
+
// sell token into BNB on ERC20-quoted pairs
|
|
123
|
+
'function sellForEth(uint256 origin, address token, uint256 amount, uint256 minFunds, uint256 feeRate, address feeRecipient)',
|
|
124
|
+
// router-style variant with custom recipient is available, but we keep simple form here
|
|
125
|
+
];
|
|
67
126
|
if (config?.debug) {
|
|
68
127
|
logger_1.LoggerFactory.configure(logger_1.LoggerFactory.createConsoleLogger({ level: logger_1.LogLevelEnum.DEBUG }));
|
|
69
128
|
}
|
|
@@ -72,8 +131,10 @@ class FourMemeService {
|
|
|
72
131
|
logger_1.LoggerFactory.configure(config.logger);
|
|
73
132
|
}
|
|
74
133
|
logger = logger_1.LoggerFactory.getLogger();
|
|
75
|
-
this.
|
|
76
|
-
|
|
134
|
+
this._viem = (0, viem_1.createPublicClient)({
|
|
135
|
+
chain: chains_1.bsc,
|
|
136
|
+
transport: (0, viem_1.http)(config.bscRpcUrl),
|
|
137
|
+
});
|
|
77
138
|
}
|
|
78
139
|
isCorrectConfig(config) {
|
|
79
140
|
return (config && Object.keys(config).length > 0 && Object.values(config).every(value => value !== ''));
|
|
@@ -83,23 +144,89 @@ class FourMemeService {
|
|
|
83
144
|
logger.debug(`Fetching price from ${this.protocol}`);
|
|
84
145
|
try {
|
|
85
146
|
const { isBuying, tokenAddress } = this._determineSwapDirection(params);
|
|
147
|
+
const helperAddr = this._tokenManagerHelperV3;
|
|
148
|
+
const tokenAddr = tokenAddress;
|
|
149
|
+
// Small local helper: parse decimal string/number to 18-decimal BigInt safely (no JS float errors)
|
|
150
|
+
const toScaled18 = (val) => {
|
|
151
|
+
const s = String(val);
|
|
152
|
+
const [i, f = ''] = s.split('.');
|
|
153
|
+
const DEC = 18n;
|
|
154
|
+
const ip = BigInt(i || '0');
|
|
155
|
+
const frac = f.slice(0, 18).padEnd(18, '0'); // truncate/pad to 18
|
|
156
|
+
return ip * 10n ** DEC + BigInt(frac || '0');
|
|
157
|
+
};
|
|
158
|
+
// Always get tokenInfo first so we know the actual quote token
|
|
159
|
+
const tokenInfo = (await this._viem.readContract({
|
|
160
|
+
address: helperAddr,
|
|
161
|
+
abi: this._helperViemAbi,
|
|
162
|
+
functionName: 'getTokenInfo',
|
|
163
|
+
args: [tokenAddr],
|
|
164
|
+
}));
|
|
165
|
+
const version = tokenInfo[0];
|
|
166
|
+
const tokenManager = tokenInfo[1];
|
|
167
|
+
const quoteAddr = tokenInfo[2] ?? constants_1.ZERO_ADDRESS;
|
|
168
|
+
const liquidityAdded = tokenInfo[11];
|
|
169
|
+
if (liquidityAdded) {
|
|
170
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, `Four.meme token has already bonded`);
|
|
171
|
+
}
|
|
86
172
|
if (isBuying) {
|
|
87
|
-
|
|
88
|
-
|
|
173
|
+
// Determine the funds to feed into tryBuy:
|
|
174
|
+
// - If the pair is BNB-quoted (quote == 0), funds are BNB (params.amountIn)
|
|
175
|
+
// - If the pair is ERC20-quoted (e.g., USDT) and user pays BNB, convert BNB->QUOTE using safe fixed-point math
|
|
176
|
+
const userPaysBNB = params.tokenIn.toLowerCase() === constants_1.ZERO_ADDRESS.toLowerCase();
|
|
177
|
+
const pairIsErc20Quoted = quoteAddr.toLowerCase() !== constants_1.ZERO_ADDRESS.toLowerCase();
|
|
178
|
+
// The BNB wei amount we'll actually spend on-chain via buyWithEth:
|
|
179
|
+
const bnbFundsWei = BigInt(this._formatAmount(params.amountIn));
|
|
180
|
+
let tryBuyFundsArg;
|
|
181
|
+
if (isBuying && userPaysBNB && pairIsErc20Quoted) {
|
|
182
|
+
// Require price overrides (source of truth for BNB/USD & QUOTE/USD)
|
|
183
|
+
const bnbUsdPrice = params?.overrideParamsFourMeme?.bnbUsdPrice;
|
|
184
|
+
const quoteTokenPriceUsd = params?.overrideParamsFourMeme?.quoteTokenPriceUsd;
|
|
185
|
+
if (!bnbUsdPrice || !quoteTokenPriceUsd) {
|
|
186
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, 'Missing price information for Four Meme token');
|
|
187
|
+
}
|
|
188
|
+
// Convert: quoteFunds = bnbFundsWei * (BNB/USD) / (QUOTE/USD), all in 1e18 fixed point
|
|
189
|
+
const bnbUsd18 = toScaled18(bnbUsdPrice); // USD per 1 BNB, 1e18
|
|
190
|
+
const quoteUsd18 = toScaled18(quoteTokenPriceUsd); // USD per 1 QUOTE, 1e18
|
|
191
|
+
const quoteFunds = (bnbFundsWei * bnbUsd18) / quoteUsd18; // 1e18
|
|
192
|
+
tryBuyFundsArg = BigInt(this._formatAmount(quoteFunds.toString())); // GWEI align
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// BNB-quoted or user is already paying in quote currency
|
|
196
|
+
tryBuyFundsArg = bnbFundsWei;
|
|
89
197
|
}
|
|
90
|
-
//
|
|
91
|
-
const buyResult = await this.
|
|
92
|
-
|
|
198
|
+
// Now pre-quote with tryBuy using the correct currency for the pair
|
|
199
|
+
const buyResult = (await this._viem.readContract({
|
|
200
|
+
address: helperAddr,
|
|
201
|
+
abi: this._helperViemAbi,
|
|
202
|
+
functionName: 'tryBuy',
|
|
203
|
+
args: [tokenAddr, 0n, tryBuyFundsArg],
|
|
204
|
+
}));
|
|
205
|
+
const estimatedAmt = buyResult[2]; // token out
|
|
206
|
+
const amountMsgVal = buyResult[5]; // not used for buyWithEth on ERC20-quoted
|
|
207
|
+
const amountFunds = buyResult[7]; // TM funds
|
|
93
208
|
const fourMemePriceResponse = {
|
|
94
209
|
routeSummary: {
|
|
95
210
|
tokenIn: params.tokenIn,
|
|
96
211
|
amountIn: params.amountIn,
|
|
97
212
|
tokenOut: params.tokenOut,
|
|
98
|
-
amountOut:
|
|
99
|
-
gas: '200000',
|
|
213
|
+
amountOut: estimatedAmt.toString(),
|
|
214
|
+
gas: '200000',
|
|
100
215
|
route: [{ protocol: 'four.meme', percentage: 100 }],
|
|
101
216
|
},
|
|
102
|
-
routerAddress:
|
|
217
|
+
routerAddress: String(tokenManager),
|
|
218
|
+
pairTokens: [tokenAddress, String(quoteAddr)],
|
|
219
|
+
liquidityAdded,
|
|
220
|
+
version: Number(version),
|
|
221
|
+
// Hints used by fetchQuote to build the exact buyWithEth
|
|
222
|
+
helperHints: {
|
|
223
|
+
estimatedAmount: estimatedAmt.toString(),
|
|
224
|
+
amountFunds: amountFunds.toString(), // informational (quote-side funds fed to tryBuy)
|
|
225
|
+
amountMsgValue: amountMsgVal.toString(), // informational
|
|
226
|
+
fundsWeiUsed: (params.tokenIn.toLowerCase() === constants_1.ZERO_ADDRESS.toLowerCase()
|
|
227
|
+
? BigInt(this._formatAmount(params.amountIn))
|
|
228
|
+
: 0n).toString(), // BNB to actually send with buyWithEth (only when paying BNB)
|
|
229
|
+
},
|
|
103
230
|
};
|
|
104
231
|
return {
|
|
105
232
|
protocol: this.protocol,
|
|
@@ -108,28 +235,57 @@ class FourMemeService {
|
|
|
108
235
|
tokenIn: params.tokenIn,
|
|
109
236
|
tokenOut: params.tokenOut,
|
|
110
237
|
amountIn: params.amountIn,
|
|
111
|
-
amountOut:
|
|
238
|
+
amountOut: estimatedAmt.toString(),
|
|
112
239
|
estimatedGas: '200000',
|
|
113
240
|
protocolResponse: fourMemePriceResponse,
|
|
114
241
|
slippage: params.slippage,
|
|
115
242
|
};
|
|
116
243
|
}
|
|
117
244
|
else {
|
|
118
|
-
|
|
119
|
-
|
|
245
|
+
// ===== SELL =====
|
|
246
|
+
// trySell returns funds in the PAIR QUOTE currency.
|
|
247
|
+
const sellResult = (await this._viem.readContract({
|
|
248
|
+
address: helperAddr,
|
|
249
|
+
abi: this._helperViemAbi,
|
|
250
|
+
functionName: 'trySell',
|
|
251
|
+
args: [tokenAddr, BigInt(params.amountIn)],
|
|
252
|
+
}));
|
|
253
|
+
const qAddr = sellResult[1] ?? constants_1.ZERO_ADDRESS; // pair quote token
|
|
254
|
+
const fundsOutInQuote = sellResult[2]; // amount in quote token
|
|
255
|
+
// Determine user's desired out token (must be a supported quote) and convert if needed.
|
|
256
|
+
// If the pair is ERC20-quoted (qAddr != 0) but user wants BNB out (tokenOut == 0),
|
|
257
|
+
// convert QUOTE -> BNB using 1e18 fixed-point math with provided USD prices.
|
|
258
|
+
let displayedAmountOut = fundsOutInQuote;
|
|
259
|
+
const userWantsBNBOut = params.tokenOut.toLowerCase() === constants_1.ZERO_ADDRESS.toLowerCase();
|
|
260
|
+
const pairIsErc20Quoted = qAddr.toLowerCase() !== constants_1.ZERO_ADDRESS.toLowerCase();
|
|
261
|
+
if (pairIsErc20Quoted && userWantsBNBOut) {
|
|
262
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, 'Cannot use EIP-7702 for BNB out when quote token is not BNB (use HelperV3.sellForEth instead)');
|
|
263
|
+
// const bnbUsdPrice = params?.overrideParamsFourMeme?.bnbUsdPrice;
|
|
264
|
+
// const quoteTokenPriceUsd = params?.overrideParamsFourMeme?.quoteTokenPriceUsd;
|
|
265
|
+
// if (!bnbUsdPrice || !quoteTokenPriceUsd) {
|
|
266
|
+
// throw sdkError(
|
|
267
|
+
// SdkErrorEnum.QUOTE_NOT_FOUND,
|
|
268
|
+
// 'Missing price information for Four Meme token (sell conversion)',
|
|
269
|
+
// );
|
|
270
|
+
// }
|
|
271
|
+
// const bnbUsd18 = toScaled18(bnbUsdPrice); // USD per 1 BNB, 1e18
|
|
272
|
+
// const quoteUsd18 = toScaled18(quoteTokenPriceUsd); // USD per 1 QUOTE, 1e18
|
|
273
|
+
// // BNB_out = QUOTE_out * (USD/QUOTE) / (USD/BNB)
|
|
274
|
+
// displayedAmountOut = (fundsOutInQuote * quoteUsd18) / bnbUsd18;
|
|
120
275
|
}
|
|
121
|
-
// Selling tokens for BNB
|
|
122
|
-
const sellResult = await this._helperContract['trySell'](tokenAddress, params.amountIn);
|
|
123
276
|
const fourMemePriceResponse = {
|
|
124
277
|
routeSummary: {
|
|
125
278
|
tokenIn: params.tokenIn,
|
|
126
279
|
amountIn: params.amountIn,
|
|
127
280
|
tokenOut: params.tokenOut,
|
|
128
|
-
amountOut:
|
|
129
|
-
gas: '200000',
|
|
281
|
+
amountOut: displayedAmountOut.toString(), // amountOut now matches user's requested out token
|
|
282
|
+
gas: '200000',
|
|
130
283
|
route: [{ protocol: 'four.meme', percentage: 100 }],
|
|
131
284
|
},
|
|
132
|
-
routerAddress:
|
|
285
|
+
routerAddress: String(tokenManager),
|
|
286
|
+
pairTokens: [tokenAddress, String(qAddr)],
|
|
287
|
+
liquidityAdded,
|
|
288
|
+
version: Number(version),
|
|
133
289
|
};
|
|
134
290
|
return {
|
|
135
291
|
protocol: this.protocol,
|
|
@@ -138,119 +294,197 @@ class FourMemeService {
|
|
|
138
294
|
tokenIn: params.tokenIn,
|
|
139
295
|
tokenOut: params.tokenOut,
|
|
140
296
|
amountIn: params.amountIn,
|
|
141
|
-
amountOut:
|
|
297
|
+
amountOut: displayedAmountOut.toString(),
|
|
142
298
|
estimatedGas: '200000',
|
|
143
299
|
protocolResponse: fourMemePriceResponse,
|
|
144
300
|
slippage: params.slippage,
|
|
145
301
|
};
|
|
146
302
|
}
|
|
147
303
|
}
|
|
148
|
-
catch (
|
|
149
|
-
const
|
|
150
|
-
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.PRICE_NOT_FOUND,
|
|
304
|
+
catch (e) {
|
|
305
|
+
const error = e?.message || JSON.stringify(e);
|
|
306
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.PRICE_NOT_FOUND, error);
|
|
151
307
|
}
|
|
152
308
|
}
|
|
153
309
|
async fetchQuote(params) {
|
|
154
310
|
logger.info(`Fetching swap quote for address: ${params.from}`);
|
|
155
311
|
this.validatePriceParams(params);
|
|
156
312
|
try {
|
|
157
|
-
|
|
158
|
-
if (!
|
|
159
|
-
|
|
313
|
+
let { priceResponse } = params;
|
|
314
|
+
if (!priceResponse) {
|
|
315
|
+
priceResponse = await this.fetchPrice(params);
|
|
160
316
|
}
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
317
|
+
const pr = priceResponse.protocolResponse;
|
|
318
|
+
const tokenManager = pr.routerAddress;
|
|
319
|
+
const quotedAmountOut = priceResponse.amountOut; // NOTE: For sells on ERC20-quoted pairs with BNB out, this is already BNB after conversion in fetchPrice
|
|
320
|
+
const version = pr.version;
|
|
321
|
+
const liquidityAdded = pr.liquidityAdded;
|
|
322
|
+
if (liquidityAdded) {
|
|
164
323
|
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, `Four.meme token has already bonded`);
|
|
165
324
|
}
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, `Genius does not support launchpad tokens paired with non-native ERC20 tokens`);
|
|
325
|
+
const { isBuying, tokenAddress, quoteToken } = this._determineSwapDirection(params);
|
|
326
|
+
if (!tokenManager || tokenManager === constants_1.ZERO_ADDRESS) {
|
|
327
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, `Token ${tokenAddress} is not a Four Meme token (no TokenManager)`);
|
|
170
328
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
329
|
+
const formattedFunds = this._formatAmount(params.amountIn);
|
|
330
|
+
const minOutFromQuote = this._applySlippage({
|
|
331
|
+
amount: quotedAmountOut,
|
|
332
|
+
slippage: params.slippage,
|
|
333
|
+
isMaximum: false,
|
|
334
|
+
});
|
|
335
|
+
const isNativeQuote = (0, is_native_1.isNative)(quoteToken);
|
|
336
|
+
const helperAddr = this._tokenManagerHelperV3;
|
|
176
337
|
let transactionData;
|
|
177
|
-
let amountOut;
|
|
338
|
+
let amountOut = quotedAmountOut;
|
|
178
339
|
if (isBuying) {
|
|
179
|
-
|
|
180
|
-
|
|
340
|
+
// Determine actual pair quote
|
|
341
|
+
const pairQuote = (pr.pairTokens?.[1] ?? constants_1.ZERO_ADDRESS).toLowerCase();
|
|
342
|
+
// === ERC20-quoted pair, paying with native BNB => use HelperV3.buyWithEth ===
|
|
343
|
+
if (pairQuote !== constants_1.ZERO_ADDRESS.toLowerCase() && (0, is_native_1.isNative)(quoteToken)) {
|
|
344
|
+
const helperIface = new ethers_1.ethers.Interface(this._helperV3EthAbi);
|
|
345
|
+
const hints = (pr.helperHints ?? {});
|
|
346
|
+
// Use the helper-estimated token amount as the source of truth for minOut
|
|
347
|
+
const estAmount = hints.estimatedAmount
|
|
348
|
+
? BigInt(hints.estimatedAmount)
|
|
349
|
+
: BigInt(quotedAmountOut);
|
|
350
|
+
// Use the exact BNB amount we priced with in fetchPrice; fallback to aligned user input
|
|
351
|
+
const fundsToSpendBNB = hints.fundsWeiUsed
|
|
352
|
+
? BigInt(hints.fundsWeiUsed)
|
|
353
|
+
: BigInt(this._formatAmount(params.amountIn));
|
|
354
|
+
const minAmountOut = this._minOutFromEstimated(estAmount, params.slippage ?? 0);
|
|
355
|
+
const toArg = params.receiver && params.receiver !== params.from ? params.receiver : constants_1.ZERO_ADDRESS;
|
|
356
|
+
const callData = helperIface.encodeFunctionData('buyWithEth', [
|
|
357
|
+
0n, // origin
|
|
358
|
+
tokenAddress, // token
|
|
359
|
+
toArg, // to
|
|
360
|
+
fundsToSpendBNB, // funds (BNB)
|
|
361
|
+
minAmountOut, // minAmount (tokens)
|
|
362
|
+
]);
|
|
363
|
+
transactionData = {
|
|
364
|
+
data: callData,
|
|
365
|
+
to: this._tokenManagerHelperV3,
|
|
366
|
+
value: fundsToSpendBNB.toString(), // send BNB
|
|
367
|
+
gasEstimate: '250000',
|
|
368
|
+
gasLimit: '275000',
|
|
369
|
+
};
|
|
370
|
+
amountOut = estAmount.toString();
|
|
371
|
+
// No approval for native-in
|
|
372
|
+
return {
|
|
373
|
+
protocol: this.protocol,
|
|
374
|
+
tokenIn: params.tokenIn,
|
|
375
|
+
tokenOut: params.tokenOut,
|
|
376
|
+
amountIn: params.amountIn,
|
|
377
|
+
amountOut,
|
|
378
|
+
from: params.from,
|
|
379
|
+
receiver: params.receiver || params.from,
|
|
380
|
+
evmExecutionPayload: {
|
|
381
|
+
transactionData,
|
|
382
|
+
approval: {
|
|
383
|
+
spender: constants_1.ZERO_ADDRESS,
|
|
384
|
+
token: params.tokenIn,
|
|
385
|
+
amount: '0',
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
slippage: params.slippage,
|
|
389
|
+
networkIn: params.networkIn,
|
|
390
|
+
networkOut: params.networkOut,
|
|
391
|
+
estimatedGas: transactionData.gasEstimate,
|
|
392
|
+
protocolResponse: {
|
|
393
|
+
amountIn: params.amountIn,
|
|
394
|
+
amountOut,
|
|
395
|
+
gas: transactionData.gasEstimate ?? '0',
|
|
396
|
+
data: transactionData.data,
|
|
397
|
+
routerAddress: transactionData.to,
|
|
398
|
+
tokenManagerVersion: String(version),
|
|
399
|
+
isBuying: true,
|
|
400
|
+
},
|
|
401
|
+
};
|
|
181
402
|
}
|
|
182
|
-
//
|
|
183
|
-
|
|
184
|
-
const buyResult = await this._helperContract['tryBuy'](tokenAddress, '0', // We want to spend specific amount of BNB
|
|
185
|
-
formattedAmount);
|
|
186
|
-
amountOut = buyResult.estimatedAmount.toString();
|
|
187
|
-
const minAmount = this._applySlippage(amountOut, params.slippage, false);
|
|
188
|
-
// Build transaction data based on TokenManager version
|
|
189
|
-
if (version === 1n) {
|
|
403
|
+
// === Legacy / existing paths (unchanged)
|
|
404
|
+
else if (version === 1) {
|
|
190
405
|
const tokenManagerV1Interface = new ethers_1.ethers.Interface(this._tokenManagerV1Abi);
|
|
191
406
|
const callData = params.receiver && params.receiver !== params.from
|
|
192
407
|
? tokenManagerV1Interface.encodeFunctionData('purchaseTokenAMAP', [
|
|
193
|
-
0,
|
|
408
|
+
0,
|
|
194
409
|
tokenAddress,
|
|
195
410
|
params.receiver,
|
|
196
|
-
|
|
197
|
-
|
|
411
|
+
BigInt(formattedFunds),
|
|
412
|
+
BigInt(minOutFromQuote),
|
|
198
413
|
])
|
|
199
414
|
: tokenManagerV1Interface.encodeFunctionData('purchaseTokenAMAP', [
|
|
200
415
|
tokenAddress,
|
|
201
|
-
|
|
202
|
-
|
|
416
|
+
BigInt(formattedFunds),
|
|
417
|
+
BigInt(minOutFromQuote),
|
|
203
418
|
]);
|
|
204
419
|
transactionData = {
|
|
205
420
|
data: callData,
|
|
206
|
-
to:
|
|
207
|
-
value:
|
|
421
|
+
to: tokenManager,
|
|
422
|
+
value: isNativeQuote ? formattedFunds : '0',
|
|
208
423
|
gasEstimate: '200000',
|
|
209
424
|
gasLimit: '220000',
|
|
210
425
|
};
|
|
211
426
|
}
|
|
212
427
|
else {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const callData = params.receiver && params.receiver !== params.from
|
|
428
|
+
const useSecondary = !(params.receiver && params.receiver !== params.from);
|
|
429
|
+
const tokenManagerV2Interface = new ethers_1.ethers.Interface(useSecondary ? this._tokenManagerV2SecondaryAbi : this._tokenManagerV2Abi);
|
|
430
|
+
const callData = useSecondary
|
|
217
431
|
? tokenManagerV2Interface.encodeFunctionData('buyTokenAMAP', [
|
|
218
|
-
BigInt(0),
|
|
219
432
|
tokenAddress,
|
|
220
|
-
BigInt(
|
|
221
|
-
BigInt(
|
|
433
|
+
BigInt(formattedFunds),
|
|
434
|
+
BigInt(minOutFromQuote),
|
|
222
435
|
])
|
|
223
436
|
: tokenManagerV2Interface.encodeFunctionData('buyTokenAMAP', [
|
|
437
|
+
BigInt(0),
|
|
224
438
|
tokenAddress,
|
|
225
|
-
BigInt(
|
|
226
|
-
BigInt(
|
|
439
|
+
BigInt(formattedFunds),
|
|
440
|
+
BigInt(minOutFromQuote),
|
|
227
441
|
]);
|
|
228
442
|
transactionData = {
|
|
229
443
|
data: callData,
|
|
230
|
-
to:
|
|
231
|
-
value:
|
|
444
|
+
to: tokenManager,
|
|
445
|
+
value: isNativeQuote ? formattedFunds : '0',
|
|
232
446
|
gasEstimate: '200000',
|
|
233
447
|
gasLimit: '220000',
|
|
234
448
|
};
|
|
235
449
|
}
|
|
236
450
|
}
|
|
237
451
|
else {
|
|
238
|
-
|
|
239
|
-
|
|
452
|
+
// ===== SELL =====
|
|
453
|
+
const pairQuote = (pr.pairTokens?.[1] ?? constants_1.ZERO_ADDRESS).toLowerCase();
|
|
454
|
+
// If pair is ERC20-quoted but user wants BNB out, we can pass a non-zero minFunds (in BNB)
|
|
455
|
+
const wantsBnbOut = (0, is_native_1.isNative)(params.tokenOut);
|
|
456
|
+
if (pairQuote !== constants_1.ZERO_ADDRESS.toLowerCase() && wantsBnbOut) {
|
|
457
|
+
const helperIface = new ethers_1.ethers.Interface(this._helperV3EthAbi);
|
|
458
|
+
const tokenAmountIn = BigInt(this._formatAmount(params.amountIn));
|
|
459
|
+
// quotedAmountOut was already converted to BNB in fetchPrice (BNB_out estimate)
|
|
460
|
+
const estBnbOut = BigInt(quotedAmountOut);
|
|
461
|
+
const minBnbOut = this._minOutFromEstimated(estBnbOut, params.slippage ?? 0);
|
|
462
|
+
const callData = helperIface.encodeFunctionData('sellForEth', [
|
|
463
|
+
BigInt(0), // origin
|
|
464
|
+
tokenAddress, // token
|
|
465
|
+
tokenAmountIn, // amount
|
|
466
|
+
minBnbOut, // minFunds (BNB)
|
|
467
|
+
BigInt(0), // feeRate
|
|
468
|
+
constants_1.ZERO_ADDRESS, // feeRecipient
|
|
469
|
+
]);
|
|
470
|
+
transactionData = {
|
|
471
|
+
data: callData,
|
|
472
|
+
to: helperAddr,
|
|
473
|
+
value: '0',
|
|
474
|
+
gasEstimate: '250000',
|
|
475
|
+
gasLimit: '275000',
|
|
476
|
+
};
|
|
240
477
|
}
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
const sellResult = await this._helperContract['trySell'](tokenAddress, formattedAmount);
|
|
244
|
-
amountOut = sellResult.funds.toString();
|
|
245
|
-
if (version === 1n) {
|
|
478
|
+
// === Legacy / existing paths (unchanged) ===
|
|
479
|
+
else if (version === 1) {
|
|
246
480
|
const tokenManagerV1Interface = new ethers_1.ethers.Interface(this._tokenManagerV1Abi);
|
|
247
481
|
const callData = tokenManagerV1Interface.encodeFunctionData('saleToken', [
|
|
248
482
|
tokenAddress,
|
|
249
|
-
params.amountIn,
|
|
483
|
+
BigInt(this._formatAmount(params.amountIn)),
|
|
250
484
|
]);
|
|
251
485
|
transactionData = {
|
|
252
486
|
data: callData,
|
|
253
|
-
to:
|
|
487
|
+
to: tokenManager,
|
|
254
488
|
value: '0',
|
|
255
489
|
gasEstimate: '200000',
|
|
256
490
|
gasLimit: '220000',
|
|
@@ -258,18 +492,17 @@ class FourMemeService {
|
|
|
258
492
|
}
|
|
259
493
|
else {
|
|
260
494
|
const tokenManagerV2Interface = new ethers_1.ethers.Interface(this._tokenManagerV2Abi);
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
const formattedMinAmount = this._formatAmount(minAmount);
|
|
495
|
+
const tokenAmountIn = this._formatAmount(params.amountIn);
|
|
496
|
+
const minQuoteOut = this._formatAmount(minOutFromQuote); // when not using helper, minOut is in the pair's quote currency
|
|
264
497
|
const callData = tokenManagerV2Interface.encodeFunctionData('sellToken', [
|
|
265
|
-
BigInt(
|
|
266
|
-
|
|
267
|
-
BigInt(
|
|
268
|
-
BigInt(
|
|
498
|
+
BigInt(0),
|
|
499
|
+
tokenAddress,
|
|
500
|
+
BigInt(tokenAmountIn),
|
|
501
|
+
BigInt(minQuoteOut),
|
|
269
502
|
]);
|
|
270
503
|
transactionData = {
|
|
271
504
|
data: callData,
|
|
272
|
-
to:
|
|
505
|
+
to: tokenManager,
|
|
273
506
|
value: '0',
|
|
274
507
|
gasEstimate: '200000',
|
|
275
508
|
gasLimit: '220000',
|
|
@@ -279,10 +512,10 @@ class FourMemeService {
|
|
|
279
512
|
const fourMemeQuoteResponse = {
|
|
280
513
|
amountIn: params.amountIn,
|
|
281
514
|
amountOut,
|
|
282
|
-
gas: transactionData
|
|
515
|
+
gas: transactionData.gasEstimate ?? '0',
|
|
283
516
|
data: transactionData.data,
|
|
284
|
-
routerAddress:
|
|
285
|
-
tokenManagerVersion: version
|
|
517
|
+
routerAddress: transactionData.to,
|
|
518
|
+
tokenManagerVersion: String(version),
|
|
286
519
|
isBuying,
|
|
287
520
|
};
|
|
288
521
|
return {
|
|
@@ -296,7 +529,7 @@ class FourMemeService {
|
|
|
296
529
|
evmExecutionPayload: {
|
|
297
530
|
transactionData,
|
|
298
531
|
approval: {
|
|
299
|
-
spender:
|
|
532
|
+
spender: tokenManager, // unchanged legacy spender
|
|
300
533
|
token: params.tokenIn,
|
|
301
534
|
amount: params.amountIn,
|
|
302
535
|
},
|
|
@@ -308,9 +541,9 @@ class FourMemeService {
|
|
|
308
541
|
protocolResponse: fourMemeQuoteResponse,
|
|
309
542
|
};
|
|
310
543
|
}
|
|
311
|
-
catch (
|
|
312
|
-
const
|
|
313
|
-
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND,
|
|
544
|
+
catch (e) {
|
|
545
|
+
const error = e?.message || JSON.stringify(e);
|
|
546
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, error);
|
|
314
547
|
}
|
|
315
548
|
}
|
|
316
549
|
/**
|
|
@@ -321,30 +554,33 @@ class FourMemeService {
|
|
|
321
554
|
* @returns {{ isBuying: boolean; tokenAddress: string }} The swap direction and token address.
|
|
322
555
|
*/
|
|
323
556
|
_determineSwapDirection(params) {
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
if (
|
|
327
|
-
// Buying
|
|
328
|
-
return {
|
|
557
|
+
const tokenInIsQuote = this._isQuoteAddress(params.tokenIn);
|
|
558
|
+
const tokenOutIsQuote = this._isQuoteAddress(params.tokenOut);
|
|
559
|
+
if (tokenInIsQuote && !tokenOutIsQuote) {
|
|
560
|
+
// Buying token with quote token (quote -> token)
|
|
561
|
+
return {
|
|
562
|
+
isBuying: true,
|
|
563
|
+
tokenAddress: params.tokenOut,
|
|
564
|
+
quoteToken: params.tokenIn,
|
|
565
|
+
};
|
|
329
566
|
}
|
|
330
|
-
else if (!
|
|
331
|
-
// Selling
|
|
332
|
-
return {
|
|
567
|
+
else if (!tokenInIsQuote && tokenOutIsQuote) {
|
|
568
|
+
// Selling token for quote token (token -> quote)
|
|
569
|
+
return {
|
|
570
|
+
isBuying: false,
|
|
571
|
+
tokenAddress: params.tokenIn,
|
|
572
|
+
quoteToken: params.tokenOut,
|
|
573
|
+
};
|
|
333
574
|
}
|
|
334
575
|
else {
|
|
335
|
-
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, '
|
|
576
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, 'Exactly one side must be a supported quote token (BNB/WBNB/CAKE/USDT/USD1/ASTER)');
|
|
336
577
|
}
|
|
337
578
|
}
|
|
338
579
|
/**
|
|
339
580
|
* Applies slippage to an amount.
|
|
340
|
-
*
|
|
341
|
-
* @param {string} amount - The base amount.
|
|
342
|
-
* @param {number} slippage - The slippage percentage (0.01 = 1%).
|
|
343
|
-
* @param {boolean} isMaximum - Whether to calculate maximum (true) or minimum (false) amount.
|
|
344
|
-
*
|
|
345
|
-
* @returns {string} The amount with slippage applied.
|
|
346
581
|
*/
|
|
347
|
-
_applySlippage(
|
|
582
|
+
_applySlippage(params) {
|
|
583
|
+
const { amount, slippage, isMaximum } = params;
|
|
348
584
|
const amountBN = BigInt(amount);
|
|
349
585
|
const slippageBps = Math.floor(slippage * 100); // Convert percentage to basis points (10% = 1000 bps)
|
|
350
586
|
const base = BigInt(10000);
|
|
@@ -353,39 +589,33 @@ class FourMemeService {
|
|
|
353
589
|
}
|
|
354
590
|
/**
|
|
355
591
|
* Formats the given amount string to ensure it is divisible by 1 GWEI (10^9 wei).
|
|
356
|
-
*
|
|
357
|
-
* This method is used to avoid "GWEI" errors from the four meme contracts by aligning
|
|
358
|
-
* the amount to the nearest lower value that is divisible by GWEI. If the input amount
|
|
359
|
-
* is already divisible by GWEI, it is returned as-is. If the aligned amount would be zero,
|
|
360
|
-
* the method returns the minimum value of 1 GWEI.
|
|
361
|
-
*
|
|
362
|
-
* @param amount - The amount to format, represented as a string.
|
|
363
|
-
* @returns The formatted amount as a string, guaranteed to be divisible by 1 GWEI and never zero.
|
|
592
|
+
* Avoids "GW - GWEI" errors.
|
|
364
593
|
*/
|
|
365
594
|
_formatAmount(amount) {
|
|
366
|
-
/**
|
|
367
|
-
* to avoid "GWEI" errors from the four meme contracts
|
|
368
|
-
*/
|
|
369
595
|
const amountBigInt = BigInt(amount);
|
|
370
596
|
const GWEI = BigInt(10 ** 9); // 1 GWEI = 10^9 wei
|
|
371
|
-
// Check if already divisible by GWEI
|
|
372
597
|
if (amountBigInt % GWEI === BigInt(0)) {
|
|
373
|
-
return amount; // Already aligned
|
|
598
|
+
return amount; // Already aligned
|
|
374
599
|
}
|
|
375
|
-
// Find the closest value divisible by GWEI that's <= original amount
|
|
376
600
|
const alignedAmount = (amountBigInt / GWEI) * GWEI;
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
return GWEI.toString(); // Return 1 GWEI as minimum
|
|
380
|
-
}
|
|
601
|
+
if (alignedAmount === BigInt(0))
|
|
602
|
+
return GWEI.toString();
|
|
381
603
|
return alignedAmount.toString();
|
|
382
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Determines if the provided address matches any of the predefined quote token addresses.
|
|
607
|
+
*/
|
|
608
|
+
_isQuoteAddress(addr) {
|
|
609
|
+
const a = addr.toLowerCase();
|
|
610
|
+
return (a === constants_1.ZERO_ADDRESS.toLowerCase() ||
|
|
611
|
+
a === fourmeme_direct_config_1.FourMemeQuoteTokensEnum.WBNB.toLowerCase() ||
|
|
612
|
+
a === fourmeme_direct_config_1.FourMemeQuoteTokensEnum.CAKE.toLowerCase() ||
|
|
613
|
+
a === fourmeme_direct_config_1.FourMemeQuoteTokensEnum.USDT.toLowerCase() ||
|
|
614
|
+
a === fourmeme_direct_config_1.FourMemeQuoteTokensEnum.USD1.toLowerCase() ||
|
|
615
|
+
a === fourmeme_direct_config_1.FourMemeQuoteTokensEnum.ASTER.toLowerCase());
|
|
616
|
+
}
|
|
383
617
|
/**
|
|
384
618
|
* Validates the parameters for a price quote request.
|
|
385
|
-
*
|
|
386
|
-
* @param {PriceParams} params - The parameters to validate.
|
|
387
|
-
*
|
|
388
|
-
* @throws {SdkError} If any of the parameters are invalid or unsupported.
|
|
389
619
|
*/
|
|
390
620
|
validatePriceParams(params) {
|
|
391
621
|
const { networkIn, networkOut, tokenIn, tokenOut } = params;
|
|
@@ -406,14 +636,18 @@ class FourMemeService {
|
|
|
406
636
|
logger.error(`Network ${networkOut} not supported`);
|
|
407
637
|
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `Network ${networkOut} not supported`);
|
|
408
638
|
}
|
|
409
|
-
// Validate that
|
|
410
|
-
const
|
|
411
|
-
const
|
|
412
|
-
if (
|
|
413
|
-
logger.error('
|
|
414
|
-
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, '
|
|
639
|
+
// Validate that exactly one side is a quote token
|
|
640
|
+
const tokenInIsQuote = this._isQuoteAddress(tokenIn);
|
|
641
|
+
const tokenOutIsQuote = this._isQuoteAddress(tokenOut);
|
|
642
|
+
if (tokenInIsQuote === tokenOutIsQuote) {
|
|
643
|
+
logger.error('Exactly one side must be a supported quote token');
|
|
644
|
+
throw (0, throw_error_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, 'Exactly one side must be a supported quote token (BNB/WBNB/CAKE/USDT/USD1/ASTER)');
|
|
415
645
|
}
|
|
416
646
|
}
|
|
647
|
+
_minOutFromEstimated(estimated, slippage) {
|
|
648
|
+
const bps = Math.floor(slippage * 100);
|
|
649
|
+
return (estimated * BigInt(10000 - bps)) / 10000n;
|
|
650
|
+
}
|
|
417
651
|
}
|
|
418
652
|
exports.FourMemeService = FourMemeService;
|
|
419
653
|
//# sourceMappingURL=four-meme.service.js.map
|