@clonegod/ttd-sui-common 1.0.101 → 2.0.1
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/dist/appconfig/SuiQuoteAppConfig.d.ts +10 -0
- package/dist/appconfig/SuiQuoteAppConfig.js +35 -0
- package/dist/appconfig/SuiTradeAppConfig.d.ts +7 -0
- package/dist/appconfig/SuiTradeAppConfig.js +13 -0
- package/dist/appconfig/ensure_core_env.d.ts +1 -0
- package/dist/appconfig/ensure_core_env.js +18 -0
- package/dist/appconfig/index.d.ts +5 -0
- package/dist/appconfig/index.js +21 -0
- package/dist/appconfig/sui_dex_env_args.d.ts +5 -0
- package/dist/appconfig/sui_dex_env_args.js +28 -0
- package/dist/appconfig/sui_env_args.d.ts +4 -0
- package/dist/appconfig/sui_env_args.js +20 -0
- package/dist/grpc/gas-price-cache.js +19 -32
- package/dist/grpc/grpc-connection.js +5 -3
- package/dist/grpc/grpc_provider_registry.d.ts +14 -0
- package/dist/grpc/grpc_provider_registry.js +60 -0
- package/dist/grpc/index.d.ts +3 -0
- package/dist/grpc/index.js +13 -1
- package/dist/grpc/ledger-service.js +107 -128
- package/dist/grpc/proto_value.d.ts +4 -0
- package/dist/grpc/proto_value.js +59 -0
- package/dist/grpc/state-service.d.ts +1 -0
- package/dist/grpc/state-service.js +99 -102
- package/dist/grpc/sui-grpc-client.js +2 -13
- package/dist/grpc/sui_object_reader.d.ts +15 -0
- package/dist/grpc/sui_object_reader.js +60 -0
- package/dist/grpc/transaction-service.js +26 -37
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/quote/abstract_dex_quote.d.ts +60 -0
- package/dist/quote/abstract_dex_quote.js +186 -0
- package/dist/quote/chain_ops.d.ts +17 -0
- package/dist/quote/chain_ops.js +52 -0
- package/dist/quote/index.d.ts +7 -0
- package/dist/quote/index.js +7 -0
- package/dist/quote/pool_event.d.ts +21 -0
- package/dist/quote/pool_event.js +6 -0
- package/dist/quote/pricing/token_price_cache.js +18 -29
- package/dist/quote/quote_amount.d.ts +4 -0
- package/dist/quote/quote_amount.js +24 -0
- package/dist/quote/quote_trace.d.ts +16 -0
- package/dist/quote/quote_trace.js +40 -0
- package/dist/quote/tick/clmm_v3_engine.d.ts +32 -0
- package/dist/quote/tick/clmm_v3_engine.js +48 -0
- package/dist/quote/tick/index.d.ts +4 -0
- package/dist/quote/tick/index.js +20 -0
- package/dist/quote/tick/local_clmm_state.d.ts +17 -0
- package/dist/quote/tick/local_clmm_state.js +22 -0
- package/dist/quote/tick/sui_clmm_tick_cache.d.ts +42 -0
- package/dist/quote/tick/sui_clmm_tick_cache.js +163 -0
- package/dist/quote/tick/sui_tick_data_provider.d.ts +2 -0
- package/dist/quote/tick/sui_tick_data_provider.js +6 -0
- package/dist/quote/verify/index.d.ts +1 -0
- package/dist/quote/verify/index.js +17 -0
- package/dist/quote/verify/quote_price_verify.d.ts +30 -0
- package/dist/quote/verify/quote_price_verify.js +247 -0
- package/dist/redis/redis_client.d.ts +1 -0
- package/dist/redis/redis_client.js +88 -117
- package/dist/rpc/index.js +59 -75
- package/dist/test/test.js +1 -1
- package/dist/test/test_checkpoint.js +4 -13
- package/dist/test/test_grpc.js +32 -41
- package/dist/trade/abstract_sui_dex_trade.d.ts +43 -0
- package/dist/trade/abstract_sui_dex_trade.js +380 -0
- package/dist/trade/abstract_sui_dex_trade_plus.d.ts +3 -1
- package/dist/trade/abstract_sui_dex_trade_plus.js +232 -212
- package/dist/trade/check/tx_result_checker.js +65 -75
- package/dist/trade/coin/index.d.ts +1 -0
- package/dist/trade/coin/index.js +17 -0
- package/dist/trade/coin/lua_scripts.d.ts +5 -0
- package/dist/trade/coin/lua_scripts.js +130 -0
- package/dist/trade/coin/types.d.ts +30 -0
- package/dist/trade/coin/types.js +2 -0
- package/dist/trade/coin/wallet_coin_ledger.d.ts +22 -0
- package/dist/trade/coin/wallet_coin_ledger.js +85 -0
- package/dist/trade/executor/central_executor.d.ts +72 -0
- package/dist/trade/executor/central_executor.js +240 -0
- package/dist/trade/executor/coin_cache.d.ts +21 -0
- package/dist/trade/executor/coin_cache.js +143 -0
- package/dist/trade/executor/coin_maintainer.d.ts +32 -0
- package/dist/trade/executor/coin_maintainer.js +123 -0
- package/dist/trade/executor/core_channel.d.ts +38 -0
- package/dist/trade/executor/core_channel.js +131 -0
- package/dist/trade/executor/data_channel.d.ts +27 -0
- package/dist/trade/executor/data_channel.js +2 -0
- package/dist/trade/executor/effects.d.ts +16 -0
- package/dist/trade/executor/effects.js +63 -0
- package/dist/trade/executor/executor_client.d.ts +13 -0
- package/dist/trade/executor/executor_client.js +55 -0
- package/dist/trade/executor/executor_protocol.d.ts +26 -0
- package/dist/trade/executor/executor_protocol.js +32 -0
- package/dist/trade/executor/executor_server.d.ts +8 -0
- package/dist/trade/executor/executor_server.js +33 -0
- package/dist/trade/executor/executor_ws_client.d.ts +13 -0
- package/dist/trade/executor/executor_ws_client.js +58 -0
- package/dist/trade/executor/grpc_channel.d.ts +14 -0
- package/dist/trade/executor/grpc_channel.js +73 -0
- package/dist/trade/executor/index.d.ts +7 -0
- package/dist/trade/executor/index.js +23 -0
- package/dist/trade/executor/json_rpc_channel.d.ts +14 -0
- package/dist/trade/executor/json_rpc_channel.js +77 -0
- package/dist/trade/index.d.ts +5 -1
- package/dist/trade/index.js +5 -1
- package/dist/trade/parse/sui_tx_parser.js +98 -81
- package/dist/trade/send_tx/index.js +34 -47
- package/dist/trade/swap/builders/bluefin.d.ts +9 -0
- package/dist/trade/swap/builders/bluefin.js +60 -0
- package/dist/trade/swap/builders/cetus_magma.d.ts +13 -0
- package/dist/trade/swap/builders/cetus_magma.js +52 -0
- package/dist/trade/swap/builders/momentum.d.ts +9 -0
- package/dist/trade/swap/builders/momentum.js +80 -0
- package/dist/trade/swap/dex_swap_config.d.ts +28 -0
- package/dist/trade/swap/dex_swap_config.js +40 -0
- package/dist/trade/swap/index.d.ts +7 -0
- package/dist/trade/swap/index.js +36 -0
- package/dist/trade/swap/types.d.ts +20 -0
- package/dist/trade/swap/types.js +2 -0
- package/dist/trade/test/test_parse_sui_tx_result.js +33 -44
- package/dist/trade/tx_result_channel.d.ts +7 -0
- package/dist/trade/tx_result_channel.js +7 -0
- package/dist/utils/decode.js +1 -2
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/trade_direction.d.ts +14 -0
- package/dist/utils/trade_direction.js +23 -0
- package/package.json +3 -2
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -20,6 +11,8 @@ const transactions_1 = require("@mysten/sui/transactions");
|
|
|
20
11
|
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
21
12
|
const index_1 = require("../index");
|
|
22
13
|
const redis_1 = require("../redis");
|
|
14
|
+
const executor_1 = require("./executor");
|
|
15
|
+
const tx_result_channel_1 = require("./tx_result_channel");
|
|
23
16
|
class AbstractSuiDexTradePlus extends dist_1.AbastrcatTrade {
|
|
24
17
|
constructor(appConfig, grpcClient) {
|
|
25
18
|
super();
|
|
@@ -31,26 +24,36 @@ class AbstractSuiDexTradePlus extends dist_1.AbastrcatTrade {
|
|
|
31
24
|
this.chainNameLower = this.appConfig.env_args.chain_id.toLowerCase();
|
|
32
25
|
this.redisClient = new redis_1.SimpleRedisClient(`${this.chainNameLower}:tx`);
|
|
33
26
|
}
|
|
34
|
-
init() {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
async init() {
|
|
28
|
+
this.initSuiClient();
|
|
29
|
+
await this.initConfigs();
|
|
30
|
+
const walletGroupIds = process.env.SUI_WALLET_GROUP_IDS || process.env.WALLET_GROUP_IDS || '';
|
|
31
|
+
this.walletMode = walletGroupIds && walletGroupIds.trim().split(',').length > 0 ? 'multi' : 'single';
|
|
32
|
+
if (this.walletMode === 'multi') {
|
|
33
|
+
let wallet_infos = (0, dist_1.load_wallet_multi)(walletGroupIds.split(','), false);
|
|
34
|
+
this.group_wallets = wallet_infos.map(info => ed25519_1.Ed25519Keypair.fromSecretKey(info.private_key));
|
|
35
|
+
this.walletAddresses = this.group_wallets.map(keypair => keypair.getPublicKey().toSuiAddress());
|
|
36
|
+
(0, dist_1.log_info)(`init wallet mode: multi, wallet count: ${this.group_wallets.length}, walletAddresses: ${this.walletAddresses}`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const { private_key } = this.appConfig.trade_runtime.wallet;
|
|
40
|
+
this.wallet = ed25519_1.Ed25519Keypair.fromSecretKey(private_key);
|
|
41
|
+
this.currentWalletAddress = this.wallet.getPublicKey().toSuiAddress();
|
|
42
|
+
this.walletAddresses = [this.currentWalletAddress];
|
|
43
|
+
(0, dist_1.log_info)(`init wallet mode: single, walletAddress= ${this.currentWalletAddress}`);
|
|
44
|
+
}
|
|
45
|
+
this.transactionSender = new index_1.SuiTxSender(this.appConfig, this.sui_client, this.grpcClient);
|
|
46
|
+
this.executorClient = new executor_1.ExecutorWsClient();
|
|
47
|
+
const txResultChannel = (0, tx_result_channel_1.get_sui_tx_result_channel)();
|
|
48
|
+
this.appConfig.arb_cache.redis_cmd.subscribe(txResultChannel, (message) => {
|
|
49
|
+
try {
|
|
50
|
+
const msg = JSON.parse(message);
|
|
51
|
+
if (msg.digest)
|
|
52
|
+
this.appConfig.emit(`SUI_TX_RESULT_${msg.digest}`, msg.digest);
|
|
45
53
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.wallet = ed25519_1.Ed25519Keypair.fromSecretKey(private_key);
|
|
49
|
-
this.currentWalletAddress = this.wallet.getPublicKey().toSuiAddress();
|
|
50
|
-
this.walletAddresses = [this.currentWalletAddress];
|
|
51
|
-
(0, dist_1.log_info)(`init wallet mode: single, walletAddress= ${this.currentWalletAddress}`);
|
|
54
|
+
catch (e) {
|
|
55
|
+
(0, dist_1.log_warn)(`[trade] bad tx result msg`, message);
|
|
52
56
|
}
|
|
53
|
-
this.transactionSender = new index_1.SuiTxSender(this.appConfig, this.sui_client, this.grpcClient);
|
|
54
57
|
});
|
|
55
58
|
}
|
|
56
59
|
initSuiClient() {
|
|
@@ -76,152 +79,149 @@ class AbstractSuiDexTradePlus extends dist_1.AbastrcatTrade {
|
|
|
76
79
|
getWalletAddresses() {
|
|
77
80
|
return this.walletAddresses;
|
|
78
81
|
}
|
|
79
|
-
chooseWallet(context) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
async chooseWallet(context) {
|
|
83
|
+
if (this.getWalletMode() === 'single') {
|
|
84
|
+
return this.getSingleWallet();
|
|
85
|
+
}
|
|
86
|
+
const lockIdentifier = `choose_wallet`;
|
|
87
|
+
const lockKey = `${this.chainNameLower}:lock:${lockIdentifier}`;
|
|
88
|
+
const lockValue = `${Math.random().toString(36).substring(2, 15)}`;
|
|
89
|
+
let lockAcquired = false;
|
|
90
|
+
const start_time = Date.now();
|
|
91
|
+
const maxRetryTime = 200;
|
|
92
|
+
const retryDelay = 20;
|
|
93
|
+
do {
|
|
94
|
+
lockAcquired = await this.redisClient.acquireLock(lockKey, lockValue, 2);
|
|
95
|
+
if (lockAcquired) {
|
|
96
|
+
break;
|
|
84
97
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
} while (!lockAcquired && Date.now() - start_time < maxRetryTime);
|
|
99
|
-
if (!lockAcquired) {
|
|
100
|
-
throw new Error(`choose wallet, acquire lock failed: ${lockKey}, took ${Date.now() - start_time}ms`);
|
|
98
|
+
await (0, dist_1.sleep)(retryDelay);
|
|
99
|
+
} while (!lockAcquired && Date.now() - start_time < maxRetryTime);
|
|
100
|
+
if (!lockAcquired) {
|
|
101
|
+
throw new Error(`choose wallet, acquire lock failed: ${lockKey}, took ${Date.now() - start_time}ms`);
|
|
102
|
+
}
|
|
103
|
+
(0, dist_1.log_info)(`choose wallet, acquire lock success: ${lockKey}, took ${Date.now() - start_time}ms`);
|
|
104
|
+
try {
|
|
105
|
+
const { inputToken } = this.determineInputOutputTokens(context.order_msg, context.pool_info);
|
|
106
|
+
console.log('inputToken', inputToken);
|
|
107
|
+
const requiredAmount = new decimal_js_1.default(context.order_msg.amount);
|
|
108
|
+
const wallet_assets = await this.redisClient.hgetall('sui:wallet:assets');
|
|
109
|
+
if (!wallet_assets || Object.keys(wallet_assets).length === 0) {
|
|
110
|
+
throw new Error('没有找到钱包资产信息,请确保钱包监控服务正在运行');
|
|
101
111
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const requiredAmount = new decimal_js_1.default(context.order_msg.amount);
|
|
107
|
-
const wallet_assets = yield this.redisClient.hgetall('sui:wallet:assets');
|
|
108
|
-
if (!wallet_assets || Object.keys(wallet_assets).length === 0) {
|
|
109
|
-
throw new Error('没有找到钱包资产信息,请确保钱包监控服务正在运行');
|
|
112
|
+
const wallet_assets_map = {};
|
|
113
|
+
for (const [walletAddress, assetsJson] of Object.entries(wallet_assets)) {
|
|
114
|
+
try {
|
|
115
|
+
wallet_assets_map[walletAddress] = JSON.parse(assetsJson);
|
|
110
116
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
wallet_assets_map[walletAddress] = JSON.parse(assetsJson);
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
console.warn(`解析钱包资产数据失败: ${walletAddress}`, error);
|
|
118
|
-
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.warn(`解析钱包资产数据失败: ${walletAddress}`, error);
|
|
119
119
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
balance: tokenBalance
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
console.log(`钱包 ${walletAddress} 余额不足: ${tokenBalance} ${inputToken.symbol} < ${requiredAmount} ${inputToken.symbol}`);
|
|
139
|
-
}
|
|
140
|
-
allWallets.push({
|
|
120
|
+
}
|
|
121
|
+
const availableWallets = [];
|
|
122
|
+
const allWallets = [];
|
|
123
|
+
for (const [walletAddress, assets] of Object.entries(wallet_assets_map)) {
|
|
124
|
+
try {
|
|
125
|
+
const wallet = this.group_wallets?.find(w => w.getPublicKey().toSuiAddress().toLowerCase() === walletAddress.toLowerCase());
|
|
126
|
+
if (!wallet) {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const tokenAsset = assets.tokens?.find((token) => (0, index_1.normalizeSuiTokenAddress)(token.address) === (0, index_1.normalizeSuiTokenAddress)(inputToken.address));
|
|
130
|
+
let tokenBalance = tokenAsset?.balance || '0';
|
|
131
|
+
const balanceDecimal = new decimal_js_1.default(tokenBalance);
|
|
132
|
+
if (balanceDecimal.gte(requiredAmount)) {
|
|
133
|
+
availableWallets.push({
|
|
141
134
|
wallet,
|
|
142
135
|
balance: tokenBalance
|
|
143
136
|
});
|
|
144
137
|
}
|
|
145
|
-
|
|
146
|
-
console.
|
|
138
|
+
else {
|
|
139
|
+
console.log(`钱包 ${walletAddress} 余额不足: ${tokenBalance} ${inputToken.symbol} < ${requiredAmount} ${inputToken.symbol}`);
|
|
147
140
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
(0, dist_1.log_warn)(error_msg);
|
|
152
|
-
allWallets.forEach((walletInfo, index) => {
|
|
153
|
-
const walletAddress = walletInfo.wallet.getPublicKey().toSuiAddress();
|
|
154
|
-
const shortAddress = walletAddress.substring(0, 6) + '...' + walletAddress.substring(walletAddress.length - 4);
|
|
155
|
-
console.log(`${index + 1}. ${shortAddress}: ${walletInfo.balance} ${inputToken.symbol}`);
|
|
141
|
+
allWallets.push({
|
|
142
|
+
wallet,
|
|
143
|
+
balance: tokenBalance
|
|
156
144
|
});
|
|
157
|
-
throw new Error(error_msg);
|
|
158
145
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
146
|
+
catch (error) {
|
|
147
|
+
console.warn(`处理钱包资产时出错: ${walletAddress}`, error);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (availableWallets.length === 0) {
|
|
151
|
+
let error_msg = `all wallets are insufficient, required: ${requiredAmount} ${inputToken.symbol}`;
|
|
152
|
+
(0, dist_1.log_warn)(error_msg);
|
|
153
|
+
allWallets.forEach((walletInfo, index) => {
|
|
154
|
+
const walletAddress = walletInfo.wallet.getPublicKey().toSuiAddress();
|
|
155
|
+
const shortAddress = walletAddress.substring(0, 6) + '...' + walletAddress.substring(walletAddress.length - 4);
|
|
156
|
+
console.log(`${index + 1}. ${shortAddress}: ${walletInfo.balance} ${inputToken.symbol}`);
|
|
157
|
+
});
|
|
158
|
+
throw new Error(error_msg);
|
|
159
|
+
}
|
|
160
|
+
console.log('availableWallets', availableWallets.map(w => w.wallet.getPublicKey().toSuiAddress()));
|
|
161
|
+
const wallet_last_used = await this.redisClient.hgetall('sui:wallet:last_used');
|
|
162
|
+
for (const availableWallet of availableWallets) {
|
|
163
|
+
const walletAddress = availableWallet.wallet.getPublicKey().toSuiAddress();
|
|
164
|
+
const lastUsedData = wallet_last_used?.[walletAddress];
|
|
165
|
+
if (lastUsedData) {
|
|
166
|
+
try {
|
|
167
|
+
const lastUsedInfo = JSON.parse(lastUsedData);
|
|
168
|
+
availableWallet.lastUsedAt = lastUsedInfo.lastUsedAt;
|
|
172
169
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
lastUsedAt: 0,
|
|
176
|
-
lastUsedTime: new Date(0).toISOString()
|
|
177
|
-
}), 24 * 60 * 60);
|
|
178
|
-
availableWallet.lastUsedAt = 0;
|
|
170
|
+
catch (error) {
|
|
171
|
+
console.warn(`解析钱包最后使用时间失败: ${walletAddress}`, error);
|
|
179
172
|
}
|
|
180
173
|
}
|
|
181
|
-
let selectedWallet;
|
|
182
|
-
let selectionStrategy;
|
|
183
|
-
if (availableWallets.length === 1) {
|
|
184
|
-
selectedWallet = availableWallets[0].wallet;
|
|
185
|
-
selectionStrategy = 'OnlyOne';
|
|
186
|
-
}
|
|
187
174
|
else {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return aTime - bTime;
|
|
194
|
-
});
|
|
195
|
-
selectedWallet = availableWallets[0].wallet;
|
|
196
|
-
selectionStrategy = 'MaxLastUsed';
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
availableWallets.sort((a, b) => {
|
|
200
|
-
const aBalance = new decimal_js_1.default(a.balance);
|
|
201
|
-
const bBalance = new decimal_js_1.default(b.balance);
|
|
202
|
-
return bBalance.cmp(aBalance);
|
|
203
|
-
});
|
|
204
|
-
selectedWallet = availableWallets[0].wallet;
|
|
205
|
-
selectionStrategy = 'MaxBalance';
|
|
206
|
-
}
|
|
175
|
+
await this.redisClient.hsetValue('sui:wallet:last_used', walletAddress, JSON.stringify({
|
|
176
|
+
lastUsedAt: 0,
|
|
177
|
+
lastUsedTime: new Date(0).toISOString()
|
|
178
|
+
}), 24 * 60 * 60);
|
|
179
|
+
availableWallet.lastUsedAt = 0;
|
|
207
180
|
}
|
|
208
|
-
const selectedWalletAddress = selectedWallet.getPublicKey().toSuiAddress();
|
|
209
|
-
yield this.redisClient.hsetValue('sui:wallet:last_used', selectedWalletAddress, JSON.stringify({
|
|
210
|
-
lastUsedAt: Date.now(),
|
|
211
|
-
lastUsedTime: new Date().toISOString()
|
|
212
|
-
}), 24 * 60 * 60);
|
|
213
|
-
const selectedWalletInfo = availableWallets.find(w => w.wallet.getPublicKey().toSuiAddress() === selectedWalletAddress);
|
|
214
|
-
(0, dist_1.log_info)(`Choose wallet: ${selectionStrategy} -> ${selectedWalletAddress}, balance: ${selectedWalletInfo === null || selectedWalletInfo === void 0 ? void 0 : selectedWalletInfo.balance} ${inputToken.symbol}, required: ${requiredAmount} ${inputToken.symbol}`);
|
|
215
|
-
return selectedWallet;
|
|
216
181
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
182
|
+
let selectedWallet;
|
|
183
|
+
let selectionStrategy;
|
|
184
|
+
if (availableWallets.length === 1) {
|
|
185
|
+
selectedWallet = availableWallets[0].wallet;
|
|
186
|
+
selectionStrategy = 'OnlyOne';
|
|
220
187
|
}
|
|
221
|
-
|
|
222
|
-
|
|
188
|
+
else {
|
|
189
|
+
const hasUsageHistory = wallet_last_used && Object.keys(wallet_last_used).length > 0;
|
|
190
|
+
if (hasUsageHistory) {
|
|
191
|
+
availableWallets.sort((a, b) => {
|
|
192
|
+
const aTime = a.lastUsedAt || 0;
|
|
193
|
+
const bTime = b.lastUsedAt || 0;
|
|
194
|
+
return aTime - bTime;
|
|
195
|
+
});
|
|
196
|
+
selectedWallet = availableWallets[0].wallet;
|
|
197
|
+
selectionStrategy = 'MaxLastUsed';
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
availableWallets.sort((a, b) => {
|
|
201
|
+
const aBalance = new decimal_js_1.default(a.balance);
|
|
202
|
+
const bBalance = new decimal_js_1.default(b.balance);
|
|
203
|
+
return bBalance.cmp(aBalance);
|
|
204
|
+
});
|
|
205
|
+
selectedWallet = availableWallets[0].wallet;
|
|
206
|
+
selectionStrategy = 'MaxBalance';
|
|
207
|
+
}
|
|
223
208
|
}
|
|
224
|
-
|
|
209
|
+
const selectedWalletAddress = selectedWallet.getPublicKey().toSuiAddress();
|
|
210
|
+
await this.redisClient.hsetValue('sui:wallet:last_used', selectedWalletAddress, JSON.stringify({
|
|
211
|
+
lastUsedAt: Date.now(),
|
|
212
|
+
lastUsedTime: new Date().toISOString()
|
|
213
|
+
}), 24 * 60 * 60);
|
|
214
|
+
const selectedWalletInfo = availableWallets.find(w => w.wallet.getPublicKey().toSuiAddress() === selectedWalletAddress);
|
|
215
|
+
(0, dist_1.log_info)(`Choose wallet: ${selectionStrategy} -> ${selectedWalletAddress}, balance: ${selectedWalletInfo?.balance} ${inputToken.symbol}, required: ${requiredAmount} ${inputToken.symbol}`);
|
|
216
|
+
return selectedWallet;
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
(0, dist_1.log_error)(`choose wallet failed, error: ${error.message}`, error);
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
await this.redisClient.releaseLock(lockKey, lockValue);
|
|
224
|
+
}
|
|
225
225
|
}
|
|
226
226
|
determineInputOutputTokens(order_msg, pool_info) {
|
|
227
227
|
const { aToB } = order_msg;
|
|
@@ -245,19 +245,19 @@ class AbstractSuiDexTradePlus extends dist_1.AbastrcatTrade {
|
|
|
245
245
|
let { ask, bid } = price_msg;
|
|
246
246
|
const { aToB, amount: uiAmount, price: cex_order_price } = order_msg;
|
|
247
247
|
let { inputToken, outputToken } = (0, dist_1.get_input_out_token)(pool_info, aToB);
|
|
248
|
-
let amountIn = new decimal_js_1.default(uiAmount).mul(
|
|
248
|
+
let amountIn = new decimal_js_1.default(uiAmount).mul(10 ** inputToken.decimals);
|
|
249
249
|
let slippage = slippage_bps / 10000;
|
|
250
250
|
let price;
|
|
251
251
|
let amountOut;
|
|
252
252
|
let amountOutMin;
|
|
253
253
|
if (aToB) {
|
|
254
|
-
price = cex_order_price
|
|
255
|
-
amountOut = new decimal_js_1.default(uiAmount).mul(price).mul(
|
|
254
|
+
price = cex_order_price ?? bid.price;
|
|
255
|
+
amountOut = new decimal_js_1.default(uiAmount).mul(price).mul(10 ** outputToken.decimals);
|
|
256
256
|
amountOutMin = amountOut.mul(1 - slippage);
|
|
257
257
|
}
|
|
258
258
|
else {
|
|
259
|
-
price = cex_order_price
|
|
260
|
-
amountOut = new decimal_js_1.default(uiAmount).div(price).mul(
|
|
259
|
+
price = cex_order_price ?? ask.price;
|
|
260
|
+
amountOut = new decimal_js_1.default(uiAmount).div(price).mul(10 ** outputToken.decimals);
|
|
261
261
|
amountOutMin = amountOut.mul(1 - slippage);
|
|
262
262
|
}
|
|
263
263
|
let quote_result = {
|
|
@@ -272,53 +272,49 @@ class AbstractSuiDexTradePlus extends dist_1.AbastrcatTrade {
|
|
|
272
272
|
};
|
|
273
273
|
return quote_result;
|
|
274
274
|
}
|
|
275
|
-
getWalletAssetsFromRedis(walletAddress) {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
return JSON.parse(walletAssetsJson);
|
|
284
|
-
}
|
|
285
|
-
catch (error) {
|
|
286
|
-
(0, dist_1.log_error)(`从Redis获取钱包资产信息失败`, error);
|
|
287
|
-
throw error;
|
|
275
|
+
async getWalletAssetsFromRedis(walletAddress) {
|
|
276
|
+
try {
|
|
277
|
+
const redisClient = this.appConfig.arb_cache.redis_cmd.redis_client;
|
|
278
|
+
const walletAssetsJson = await redisClient.HGET('sui:wallet:assets', walletAddress);
|
|
279
|
+
if (!walletAssetsJson) {
|
|
280
|
+
return null;
|
|
288
281
|
}
|
|
289
|
-
|
|
282
|
+
return JSON.parse(walletAssetsJson);
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
(0, dist_1.log_error)(`从Redis获取钱包资产信息失败`, error);
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
290
288
|
}
|
|
291
|
-
getObjectInfo(objectId) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
(0, dist_1.log_info)(`get object info success, cost: ${Date.now() - startTime} ms`, objectInfo);
|
|
311
|
-
return objectInfo;
|
|
312
|
-
}
|
|
313
|
-
catch (error) {
|
|
314
|
-
(0, dist_1.log_error)(`get object info failed, object_id=${objectId}`, error);
|
|
315
|
-
return null;
|
|
289
|
+
async getObjectInfo(objectId) {
|
|
290
|
+
try {
|
|
291
|
+
let startTime = Date.now();
|
|
292
|
+
const objectResponse = await this.grpcClient.ledgerService.getObject(objectId, [
|
|
293
|
+
'object_id',
|
|
294
|
+
'version',
|
|
295
|
+
'digest',
|
|
296
|
+
'owner',
|
|
297
|
+
]);
|
|
298
|
+
let objectInfo = null;
|
|
299
|
+
if (objectResponse.object) {
|
|
300
|
+
objectInfo = {
|
|
301
|
+
objectId: objectId,
|
|
302
|
+
version: objectResponse.object.version,
|
|
303
|
+
digest: objectResponse.object.digest,
|
|
304
|
+
initialSharedVersion: objectResponse.object.owner.version
|
|
305
|
+
};
|
|
316
306
|
}
|
|
317
|
-
|
|
307
|
+
(0, dist_1.log_info)(`get object info success, cost: ${Date.now() - startTime} ms`, objectInfo);
|
|
308
|
+
return objectInfo;
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
(0, dist_1.log_error)(`get object info failed, object_id=${objectId}`, error);
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
318
314
|
}
|
|
319
315
|
compareBigIntBalance(a, b) {
|
|
320
|
-
const balanceA = BigInt(
|
|
321
|
-
const balanceB = BigInt(
|
|
316
|
+
const balanceA = BigInt(a?.balance || '0');
|
|
317
|
+
const balanceB = BigInt(b?.balance || '0');
|
|
322
318
|
if (balanceA > balanceB)
|
|
323
319
|
return -1;
|
|
324
320
|
if (balanceA < balanceB)
|
|
@@ -343,18 +339,42 @@ class AbstractSuiDexTradePlus extends dist_1.AbastrcatTrade {
|
|
|
343
339
|
tx.mergeCoins(primaryCoin, other_objects);
|
|
344
340
|
(0, dist_1.log_info)(`✅ ${tokenSymbol} 进行对象合并,涉及 ${other_objects.length} 个对象`);
|
|
345
341
|
}
|
|
346
|
-
try_refresh_wallet_objects(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
342
|
+
async try_refresh_wallet_objects(wallet, txid, context, initial_delay_ms = 0, try_times = 5, try_delay_ms = 300) {
|
|
343
|
+
for (let i = 0; i < try_times; i++) {
|
|
344
|
+
let delay_ms = initial_delay_ms + i * try_delay_ms;
|
|
345
|
+
setTimeout(() => {
|
|
346
|
+
let group_id = this.appConfig.trade_runtime.group.id;
|
|
347
|
+
let coin_types = [context.pool_info.tokenA.address, context.pool_info.tokenB.address];
|
|
348
|
+
this.appConfig.arb_cache.redis_event_publisher.publish_wallet_raw_tx_event(group_id, wallet, { group_id, wallet, txid, coin_types, remark: `PRE_REFRESH_OBJECTS_${i}` });
|
|
349
|
+
}, delay_ms);
|
|
350
|
+
(0, dist_1.log_info)(`try_refresh_wallet_objects, txid=${txid}, i=${i}, delay_ms=${delay_ms}ms`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
async execute(context, _retryCount) {
|
|
354
|
+
const q = this.local_calculate_output_amt(context);
|
|
355
|
+
const { pool_info } = context;
|
|
356
|
+
const coinTypeA = (0, index_1.normalizeSuiTokenAddress)(pool_info.tokenA.address);
|
|
357
|
+
const coinTypeB = (0, index_1.normalizeSuiTokenAddress)(pool_info.tokenB.address);
|
|
358
|
+
const inAddr = (0, index_1.normalizeSuiTokenAddress)(q.inputToken.address);
|
|
359
|
+
const a2b = inAddr === coinTypeA;
|
|
360
|
+
const req = {
|
|
361
|
+
dexId: (pool_info.dex_id || this.appConfig.trade_runtime.dex_id),
|
|
362
|
+
poolId: pool_info.pool_address,
|
|
363
|
+
coinTypeA,
|
|
364
|
+
coinTypeB,
|
|
365
|
+
a2b,
|
|
366
|
+
amountIn: BigInt(q.amountIn),
|
|
367
|
+
minOut: BigInt(q.amountOutMin),
|
|
368
|
+
sqrtPriceLimit: 0n,
|
|
369
|
+
};
|
|
370
|
+
(0, dist_1.log_info)(`[trade] 转发 swap 给集中执行器`, {
|
|
371
|
+
dex_id: req.dexId, pair: pool_info.pair, a2b, amountIn: req.amountIn.toString(), minOut: req.minOut.toString(),
|
|
357
372
|
});
|
|
373
|
+
const res = await this.executorClient.requestSwap(req);
|
|
374
|
+
if (!res.submitted) {
|
|
375
|
+
throw new Error(`集中执行器提交失败: ${res.error}`);
|
|
376
|
+
}
|
|
377
|
+
return res.digest;
|
|
358
378
|
}
|
|
359
379
|
}
|
|
360
380
|
exports.AbstractSuiDexTradePlus = AbstractSuiDexTradePlus;
|