@clonegod/ttd-sol-common 2.0.67 → 2.0.68
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/SolanaQuoteAppConfig.d.ts +9 -0
- package/dist/appconfig/SolanaQuoteAppConfig.js +29 -0
- package/dist/appconfig/SolanaTradeAppConfig.d.ts +13 -0
- package/dist/{config → appconfig}/SolanaTradeAppConfig.js +25 -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/sol_dex_env_args.d.ts +5 -0
- package/dist/appconfig/sol_dex_env_args.js +29 -0
- package/dist/appconfig/sol_env_args.d.ts +17 -0
- package/dist/appconfig/sol_env_args.js +37 -0
- package/dist/common/get_wallet_token_account.js +13 -16
- package/dist/grpc/grpc_provider_registry.d.ts +14 -0
- package/dist/grpc/grpc_provider_registry.js +70 -0
- package/dist/grpc/index.d.ts +1 -0
- package/dist/grpc/index.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/quote/abstract_dex_quote.d.ts +68 -0
- package/dist/quote/abstract_dex_quote.js +208 -0
- package/dist/quote/chain_ops.d.ts +18 -0
- package/dist/quote/chain_ops.js +66 -0
- package/dist/quote/depth/clmm_depth_calculator.d.ts +42 -0
- package/dist/quote/depth/clmm_depth_calculator.js +173 -0
- package/dist/quote/depth/index.d.ts +20 -0
- package/dist/quote/depth/index.js +100 -0
- package/dist/quote/index.d.ts +9 -0
- package/dist/quote/index.js +9 -0
- package/dist/quote/pool_event.d.ts +20 -0
- package/dist/quote/pool_event.js +22 -0
- package/dist/quote/pool_subscription_registry.d.ts +4 -0
- package/dist/quote/pool_subscription_registry.js +62 -0
- 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_tick_math.d.ts +5 -0
- package/dist/quote/tick/clmm_tick_math.js +61 -0
- package/dist/quote/tick/index.d.ts +1 -0
- package/dist/{config → quote/tick}/index.js +1 -1
- 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/trade/index.d.ts +0 -1
- package/dist/trade/index.js +0 -1
- package/dist/trade/tx_builder.d.ts +1 -1
- package/dist/trade/tx_result_parse.js +1 -0
- package/dist/types/index.d.ts +9 -1
- package/dist/types/index.js +2 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +17 -0
- package/dist/utils/trade_direction.d.ts +14 -0
- package/dist/utils/trade_direction.js +23 -0
- package/package.json +4 -4
- package/src/appconfig/SolanaQuoteAppConfig.ts +55 -0
- package/src/appconfig/SolanaTradeAppConfig.ts +117 -0
- package/src/appconfig/ensure_core_env.ts +28 -0
- package/src/appconfig/index.ts +5 -0
- package/src/appconfig/sol_dex_env_args.ts +52 -0
- package/src/appconfig/sol_env_args.ts +79 -0
- package/src/common/get_wallet_token_account.ts +27 -33
- package/src/grpc/grpc_provider_registry.ts +103 -0
- package/src/grpc/index.ts +1 -0
- package/src/index.ts +3 -0
- package/src/quote/abstract_dex_quote.ts +337 -0
- package/src/quote/chain_ops.ts +91 -0
- package/src/quote/depth/clmm_depth_calculator.ts +321 -0
- package/src/quote/depth/index.ts +167 -0
- package/src/quote/index.ts +9 -0
- package/src/quote/pool_event.ts +82 -0
- package/src/quote/pool_subscription_registry.ts +81 -0
- package/src/quote/quote_amount.ts +37 -0
- package/src/quote/quote_trace.ts +56 -0
- package/src/quote/tick/clmm_tick_math.ts +77 -0
- package/src/quote/tick/index.ts +1 -0
- package/src/quote/verify/index.ts +1 -0
- package/src/quote/verify/quote_price_verify.ts +508 -0
- package/src/trade/index.ts +0 -1
- package/src/trade/tx_builder.ts +1 -1
- package/src/trade/tx_result_parse.ts +1 -0
- package/src/types/index.ts +20 -2
- package/src/utils/index.ts +1 -0
- package/src/utils/trade_direction.ts +68 -0
- package/dist/config/SolanaTradeAppConfig.d.ts +0 -10
- package/dist/config/index.d.ts +0 -1
- package/dist/trade/SolanaTradeAppConfig.d.ts +0 -8
- package/dist/trade/SolanaTradeAppConfig.js +0 -26
- package/src/config/SolanaTradeAppConfig.ts +0 -70
- package/src/config/index.ts +0 -2
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SolanaPoolAccountUpdateEventData } from '../types';
|
|
2
|
+
export interface SolPoolEvent {
|
|
3
|
+
pool_address: string;
|
|
4
|
+
dex_id: string;
|
|
5
|
+
pool_name: string;
|
|
6
|
+
blockNumber: number;
|
|
7
|
+
writeVersion?: number;
|
|
8
|
+
txHash: string;
|
|
9
|
+
blockTime: number;
|
|
10
|
+
poolAccountData: string;
|
|
11
|
+
vaultAAccountData?: string;
|
|
12
|
+
vaultBAccountData?: string;
|
|
13
|
+
subAccount?: {
|
|
14
|
+
role: string;
|
|
15
|
+
address: string;
|
|
16
|
+
data: string;
|
|
17
|
+
};
|
|
18
|
+
raw: SolanaPoolAccountUpdateEventData;
|
|
19
|
+
}
|
|
20
|
+
export declare function toSolPoolEvent(data: SolanaPoolAccountUpdateEventData): SolPoolEvent;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toSolPoolEvent = toSolPoolEvent;
|
|
4
|
+
function toSolPoolEvent(data) {
|
|
5
|
+
const d = data.data;
|
|
6
|
+
return {
|
|
7
|
+
pool_address: data.pool_address,
|
|
8
|
+
dex_id: data.dex_id,
|
|
9
|
+
pool_name: data.pool_name,
|
|
10
|
+
blockNumber: d.slot,
|
|
11
|
+
writeVersion: d.write_version,
|
|
12
|
+
txHash: d.tx_hash || 'account',
|
|
13
|
+
blockTime: data.event_time || 0,
|
|
14
|
+
poolAccountData: d.pool_account_data,
|
|
15
|
+
vaultAAccountData: d.vaultA_account_data,
|
|
16
|
+
vaultBAccountData: d.vaultB_account_data,
|
|
17
|
+
subAccount: d.sub_account
|
|
18
|
+
? { role: d.sub_account.role, address: d.sub_account.account, data: d.sub_account.account_data }
|
|
19
|
+
: undefined,
|
|
20
|
+
raw: data,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { AppConfig, StandardPoolInfoType } from '@clonegod/ttd-core/dist';
|
|
2
|
+
export declare function registerPoolSubscriptions(appConfig: AppConfig, pools: StandardPoolInfoType[], quote_app_id: string): Promise<void>;
|
|
3
|
+
export declare function unregisterPoolSubscriptions(appConfig: AppConfig, quote_app_id: string): Promise<void>;
|
|
4
|
+
export declare function attachPoolSubscriptionLifecycle(appConfig: AppConfig, pools: StandardPoolInfoType[]): Promise<void>;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerPoolSubscriptions = registerPoolSubscriptions;
|
|
4
|
+
exports.unregisterPoolSubscriptions = unregisterPoolSubscriptions;
|
|
5
|
+
exports.attachPoolSubscriptionLifecycle = attachPoolSubscriptionLifecycle;
|
|
6
|
+
const dist_1 = require("@clonegod/ttd-core/dist");
|
|
7
|
+
const grpc_provider_registry_1 = require("../grpc/grpc_provider_registry");
|
|
8
|
+
function configCenterBase(appConfig) {
|
|
9
|
+
const chain = appConfig.env_args.chain_id;
|
|
10
|
+
let host = String(appConfig.env_args.config_center_host || '127.0.0.1').trim();
|
|
11
|
+
if (!host.includes(':'))
|
|
12
|
+
host = `${host}:${dist_1.SERVICE_PORT.CONFIG_CENTER_HTTP}`;
|
|
13
|
+
return `http://${host}/${chain}/config`;
|
|
14
|
+
}
|
|
15
|
+
async function registerPoolSubscriptions(appConfig, pools, quote_app_id) {
|
|
16
|
+
const provider = await (0, grpc_provider_registry_1.resolveGrpcProvider)(appConfig.arb_cache.redis_cmd, appConfig.env_args.chain_id);
|
|
17
|
+
const provider_ids = [provider.id];
|
|
18
|
+
const base = configCenterBase(appConfig);
|
|
19
|
+
let ok = 0;
|
|
20
|
+
for (const p of pools) {
|
|
21
|
+
try {
|
|
22
|
+
await dist_1.HttpUtil.post(`${base}/quote/register`, {
|
|
23
|
+
pool_address: p.pool_address,
|
|
24
|
+
provider_ids,
|
|
25
|
+
quote_app_id,
|
|
26
|
+
pair: p.pair,
|
|
27
|
+
dex_id: p.dex_id,
|
|
28
|
+
pool_name: p.pool_name,
|
|
29
|
+
fee_rate: p.fee_rate,
|
|
30
|
+
});
|
|
31
|
+
ok++;
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
(0, dist_1.log_warn)(`[pool/subscriptions] register ${p.pool_address} failed: ${e.message}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
(0, dist_1.log_info)(`[pool/subscriptions] registered ${ok}/${pools.length} pools by ${quote_app_id} → provider=${provider.id}`);
|
|
38
|
+
}
|
|
39
|
+
async function unregisterPoolSubscriptions(appConfig, quote_app_id) {
|
|
40
|
+
try {
|
|
41
|
+
await dist_1.HttpUtil.post(`${configCenterBase(appConfig)}/quote/unregister`, { quote_app_id });
|
|
42
|
+
(0, dist_1.log_info)(`[pool/subscriptions] unregistered all pools by ${quote_app_id}`);
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
(0, dist_1.log_warn)(`[pool/subscriptions] unregister failed: ${e.message}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function attachPoolSubscriptionLifecycle(appConfig, pools) {
|
|
49
|
+
const quote_app_id = String(appConfig.env_args.app_name || '').toLowerCase() || 'sol-quote';
|
|
50
|
+
await registerPoolSubscriptions(appConfig, pools, quote_app_id);
|
|
51
|
+
let cleaned = false;
|
|
52
|
+
const cleanup = async (sig) => {
|
|
53
|
+
if (cleaned)
|
|
54
|
+
return;
|
|
55
|
+
cleaned = true;
|
|
56
|
+
(0, dist_1.log_info)(`[pool/subscriptions] ${sig} → unregister ${quote_app_id}`);
|
|
57
|
+
await unregisterPoolSubscriptions(appConfig, quote_app_id);
|
|
58
|
+
process.exit(0);
|
|
59
|
+
};
|
|
60
|
+
process.on('SIGINT', () => void cleanup('SIGINT'));
|
|
61
|
+
process.on('SIGTERM', () => void cleanup('SIGTERM'));
|
|
62
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { StandardPoolInfoType } from '@clonegod/ttd-core';
|
|
2
|
+
import Decimal from 'decimal.js';
|
|
3
|
+
export declare function getQuoteAmountUsd(poolInfo: StandardPoolInfoType): number;
|
|
4
|
+
export declare function usdToTokenUiAmount(amountInUsd: number, tokenAddress: string): Promise<Decimal>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getQuoteAmountUsd = getQuoteAmountUsd;
|
|
7
|
+
exports.usdToTokenUiAmount = usdToTokenUiAmount;
|
|
8
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
9
|
+
const decimal_js_1 = __importDefault(require("decimal.js"));
|
|
10
|
+
function getQuoteAmountUsd(poolInfo) {
|
|
11
|
+
const envValue = Number(process.env.QUOTE_AMOUNT_USD);
|
|
12
|
+
if (envValue > 0) {
|
|
13
|
+
return envValue;
|
|
14
|
+
}
|
|
15
|
+
return poolInfo.quote_amount_usd;
|
|
16
|
+
}
|
|
17
|
+
async function usdToTokenUiAmount(amountInUsd, tokenAddress) {
|
|
18
|
+
const priceMap = await (0, ttd_core_1.get_solana_token_price_info)([tokenAddress]);
|
|
19
|
+
const price = priceMap.get(tokenAddress)?.price;
|
|
20
|
+
if (!price || price === '0') {
|
|
21
|
+
throw new Error(`price not available for ${tokenAddress}`);
|
|
22
|
+
}
|
|
23
|
+
return new decimal_js_1.default(amountInUsd).div(new decimal_js_1.default(price));
|
|
24
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare class QuoteTrace {
|
|
2
|
+
readonly poolName: string;
|
|
3
|
+
readonly poolAddress: string;
|
|
4
|
+
blockNumber: number;
|
|
5
|
+
source: string;
|
|
6
|
+
private startTime;
|
|
7
|
+
private marks;
|
|
8
|
+
private data;
|
|
9
|
+
private error;
|
|
10
|
+
constructor(poolName: string, poolAddress: string, blockNumber?: number, source?: string);
|
|
11
|
+
mark(name: string): void;
|
|
12
|
+
set(key: string, value: any): void;
|
|
13
|
+
markError(stage: string, message: string): void;
|
|
14
|
+
private buildTimeline;
|
|
15
|
+
flush(): void;
|
|
16
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QuoteTrace = void 0;
|
|
4
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
5
|
+
const logger = (0, ttd_core_1.createLogger)(__filename);
|
|
6
|
+
class QuoteTrace {
|
|
7
|
+
constructor(poolName, poolAddress, blockNumber = 0, source = '') {
|
|
8
|
+
this.poolName = poolName;
|
|
9
|
+
this.poolAddress = poolAddress;
|
|
10
|
+
this.blockNumber = blockNumber;
|
|
11
|
+
this.source = source;
|
|
12
|
+
this.startTime = Date.now();
|
|
13
|
+
this.marks = [];
|
|
14
|
+
this.data = {};
|
|
15
|
+
this.error = null;
|
|
16
|
+
}
|
|
17
|
+
mark(name) {
|
|
18
|
+
this.marks.push({ name, elapsed: Date.now() - this.startTime });
|
|
19
|
+
}
|
|
20
|
+
set(key, value) { this.data[key] = value; }
|
|
21
|
+
markError(stage, message) { this.error = { stage, message }; }
|
|
22
|
+
buildTimeline() {
|
|
23
|
+
if (this.marks.length === 0)
|
|
24
|
+
return '(no marks)';
|
|
25
|
+
let prev = 0;
|
|
26
|
+
const parts = this.marks.map(m => { const d = m.elapsed - prev; prev = m.elapsed; return `${m.name}(${d}ms)`; });
|
|
27
|
+
return `${parts.join(' -> ')} = ${this.marks[this.marks.length - 1].elapsed}ms`;
|
|
28
|
+
}
|
|
29
|
+
flush() {
|
|
30
|
+
const timeline = this.buildTimeline();
|
|
31
|
+
if (this.error) {
|
|
32
|
+
logger.error(`[QUOTE FAILED] ${this.poolName} | stage=${this.error.stage} | source=${this.source} | ${this.error.message}`, new Error(this.error.message));
|
|
33
|
+
logger.info(`[QUOTE FAILED detail] ${this.poolName}`, { timeline, error_stage: this.error.stage, source: this.source, ...this.data });
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
logger.debug(`[QUOTE OK] ${this.poolName} blk=${this.blockNumber} source=${this.source}`, { timeline, ...this.data });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.QuoteTrace = QuoteTrace;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const MIN_TICK = -443636;
|
|
2
|
+
export declare const MAX_TICK = 443636;
|
|
3
|
+
export declare const MIN_SQRT_PRICE_X64 = 4295048016n;
|
|
4
|
+
export declare const MAX_SQRT_PRICE_X64 = 79226673521066979257578248091n;
|
|
5
|
+
export declare function getSqrtPriceX64FromTick(tick: number): bigint;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAX_SQRT_PRICE_X64 = exports.MIN_SQRT_PRICE_X64 = exports.MAX_TICK = exports.MIN_TICK = void 0;
|
|
4
|
+
exports.getSqrtPriceX64FromTick = getSqrtPriceX64FromTick;
|
|
5
|
+
exports.MIN_TICK = -443636;
|
|
6
|
+
exports.MAX_TICK = 443636;
|
|
7
|
+
exports.MIN_SQRT_PRICE_X64 = 4295048016n;
|
|
8
|
+
exports.MAX_SQRT_PRICE_X64 = 79226673521066979257578248091n;
|
|
9
|
+
const MaxUint128 = (1n << 128n) - 1n;
|
|
10
|
+
function mulRightShift(val, mulBy) {
|
|
11
|
+
return (val * mulBy) >> 64n;
|
|
12
|
+
}
|
|
13
|
+
function getSqrtPriceX64FromTick(tick) {
|
|
14
|
+
if (!Number.isInteger(tick)) {
|
|
15
|
+
throw new Error('tick must be integer');
|
|
16
|
+
}
|
|
17
|
+
if (tick < exports.MIN_TICK || tick > exports.MAX_TICK) {
|
|
18
|
+
throw new Error('tick must be in MIN_TICK and MAX_TICK');
|
|
19
|
+
}
|
|
20
|
+
const tickAbs = tick < 0 ? -tick : tick;
|
|
21
|
+
let ratio = (tickAbs & 0x1) !== 0 ? 18445821805675395072n : 18446744073709551616n;
|
|
22
|
+
if ((tickAbs & 0x2) !== 0)
|
|
23
|
+
ratio = mulRightShift(ratio, 18444899583751176192n);
|
|
24
|
+
if ((tickAbs & 0x4) !== 0)
|
|
25
|
+
ratio = mulRightShift(ratio, 18443055278223355904n);
|
|
26
|
+
if ((tickAbs & 0x8) !== 0)
|
|
27
|
+
ratio = mulRightShift(ratio, 18439367220385607680n);
|
|
28
|
+
if ((tickAbs & 0x10) !== 0)
|
|
29
|
+
ratio = mulRightShift(ratio, 18431993317065453568n);
|
|
30
|
+
if ((tickAbs & 0x20) !== 0)
|
|
31
|
+
ratio = mulRightShift(ratio, 18417254355718170624n);
|
|
32
|
+
if ((tickAbs & 0x40) !== 0)
|
|
33
|
+
ratio = mulRightShift(ratio, 18387811781193609216n);
|
|
34
|
+
if ((tickAbs & 0x80) !== 0)
|
|
35
|
+
ratio = mulRightShift(ratio, 18329067761203558400n);
|
|
36
|
+
if ((tickAbs & 0x100) !== 0)
|
|
37
|
+
ratio = mulRightShift(ratio, 18212142134806163456n);
|
|
38
|
+
if ((tickAbs & 0x200) !== 0)
|
|
39
|
+
ratio = mulRightShift(ratio, 17980523815641700352n);
|
|
40
|
+
if ((tickAbs & 0x400) !== 0)
|
|
41
|
+
ratio = mulRightShift(ratio, 17526086738831433728n);
|
|
42
|
+
if ((tickAbs & 0x800) !== 0)
|
|
43
|
+
ratio = mulRightShift(ratio, 16651378430235570176n);
|
|
44
|
+
if ((tickAbs & 0x1000) !== 0)
|
|
45
|
+
ratio = mulRightShift(ratio, 15030750278694412288n);
|
|
46
|
+
if ((tickAbs & 0x2000) !== 0)
|
|
47
|
+
ratio = mulRightShift(ratio, 12247334978884435968n);
|
|
48
|
+
if ((tickAbs & 0x4000) !== 0)
|
|
49
|
+
ratio = mulRightShift(ratio, 8131365268886854656n);
|
|
50
|
+
if ((tickAbs & 0x8000) !== 0)
|
|
51
|
+
ratio = mulRightShift(ratio, 3584323654725218816n);
|
|
52
|
+
if ((tickAbs & 0x10000) !== 0)
|
|
53
|
+
ratio = mulRightShift(ratio, 696457651848324352n);
|
|
54
|
+
if ((tickAbs & 0x20000) !== 0)
|
|
55
|
+
ratio = mulRightShift(ratio, 26294789957507116n);
|
|
56
|
+
if ((tickAbs & 0x40000) !== 0)
|
|
57
|
+
ratio = mulRightShift(ratio, 37481735321082n);
|
|
58
|
+
if (tick > 0)
|
|
59
|
+
ratio = MaxUint128 / ratio;
|
|
60
|
+
return ratio;
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './clmm_tick_math';
|
|
@@ -14,4 +14,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./clmm_tick_math"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './quote_price_verify';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./quote_price_verify"), exports);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { QuoteTier } from '@clonegod/ttd-core';
|
|
2
|
+
export interface CheckSwapParams {
|
|
3
|
+
poolAddress: string;
|
|
4
|
+
poolName: string;
|
|
5
|
+
blockNumber: number;
|
|
6
|
+
txHash: string;
|
|
7
|
+
amount0: string;
|
|
8
|
+
amount1: string;
|
|
9
|
+
token0Address: string;
|
|
10
|
+
token1Address: string;
|
|
11
|
+
token0Decimals: number;
|
|
12
|
+
token1Decimals: number;
|
|
13
|
+
poolInfo: {
|
|
14
|
+
tokenA: any;
|
|
15
|
+
tokenB: any;
|
|
16
|
+
quote_token: string;
|
|
17
|
+
};
|
|
18
|
+
swapperDeltaConvention?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare class QuotePriceVerify {
|
|
21
|
+
private quoteCache;
|
|
22
|
+
private get enabled();
|
|
23
|
+
cacheQuote(poolAddress: string, priceId: string, source: string, askPrice: number, bidPrice: number, blockNumber: number, quoteAmountUsd: number, token0PriceUsd?: number, token1PriceUsd?: number, tiers?: {
|
|
24
|
+
askTiers?: QuoteTier[];
|
|
25
|
+
bidTiers?: QuoteTier[];
|
|
26
|
+
}): void;
|
|
27
|
+
checkSwap(params: CheckSwapParams): string | void;
|
|
28
|
+
private deriveSwapDirection;
|
|
29
|
+
private compareAndLog;
|
|
30
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QuotePriceVerify = void 0;
|
|
4
|
+
const trade_direction_1 = require("../../utils/trade_direction");
|
|
5
|
+
const ttd_core_1 = require("@clonegod/ttd-core");
|
|
6
|
+
function pickMatchingTier(tiers, actualAmountIn) {
|
|
7
|
+
if (!tiers || tiers.length === 0)
|
|
8
|
+
return null;
|
|
9
|
+
if (tiers.length === 1)
|
|
10
|
+
return { tier: tiers[0], mode: 'single_tier', lo_pct: tiers[0].pct, hi_pct: tiers[0].pct };
|
|
11
|
+
const sorted = [...tiers].sort((a, b) => a.amount_in - b.amount_in);
|
|
12
|
+
const top = sorted[sorted.length - 1];
|
|
13
|
+
if (actualAmountIn < sorted[0].amount_in) {
|
|
14
|
+
return { tier: sorted[0], mode: 'extrapolated_low', lo_pct: null, hi_pct: sorted[0].pct };
|
|
15
|
+
}
|
|
16
|
+
if (actualAmountIn > top.amount_in) {
|
|
17
|
+
return { tier: top, mode: 'extrapolated_high', lo_pct: top.pct, hi_pct: null };
|
|
18
|
+
}
|
|
19
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
20
|
+
const lower = sorted[i];
|
|
21
|
+
const upper = sorted[i + 1];
|
|
22
|
+
if (actualAmountIn >= lower.amount_in && actualAmountIn <= upper.amount_in) {
|
|
23
|
+
if (actualAmountIn === lower.amount_in)
|
|
24
|
+
return { tier: lower, mode: 'exact', lo_pct: lower.pct, hi_pct: lower.pct };
|
|
25
|
+
if (actualAmountIn === upper.amount_in)
|
|
26
|
+
return { tier: upper, mode: 'exact', lo_pct: upper.pct, hi_pct: upper.pct };
|
|
27
|
+
const span = upper.amount_in - lower.amount_in;
|
|
28
|
+
if (span <= 0)
|
|
29
|
+
return { tier: lower, mode: 'exact', lo_pct: lower.pct, hi_pct: lower.pct };
|
|
30
|
+
const ratio = (actualAmountIn - lower.amount_in) / span;
|
|
31
|
+
const lerp = (a, b) => a + ratio * (b - a);
|
|
32
|
+
return {
|
|
33
|
+
tier: {
|
|
34
|
+
pct: lerp(lower.pct, upper.pct),
|
|
35
|
+
price: lerp(lower.price, upper.price),
|
|
36
|
+
amount: lerp(lower.amount, upper.amount),
|
|
37
|
+
amount_in: actualAmountIn,
|
|
38
|
+
amount_in_usd: lerp(lower.amount_in_usd, upper.amount_in_usd),
|
|
39
|
+
fee: lerp(lower.fee, upper.fee),
|
|
40
|
+
fee_usd: lerp(lower.fee_usd, upper.fee_usd),
|
|
41
|
+
},
|
|
42
|
+
mode: 'interpolated',
|
|
43
|
+
lo_pct: lower.pct,
|
|
44
|
+
hi_pct: upper.pct,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { tier: top, mode: 'extrapolated_high', lo_pct: top.pct, hi_pct: null };
|
|
49
|
+
}
|
|
50
|
+
class QuotePriceVerify {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.quoteCache = new Map();
|
|
53
|
+
}
|
|
54
|
+
get enabled() {
|
|
55
|
+
return process.env.VERIFY_QUOTE_PRICE === 'true';
|
|
56
|
+
}
|
|
57
|
+
cacheQuote(poolAddress, priceId, source, askPrice, bidPrice, blockNumber, quoteAmountUsd, token0PriceUsd = 0, token1PriceUsd = 0, tiers) {
|
|
58
|
+
if (!this.enabled)
|
|
59
|
+
return;
|
|
60
|
+
if (!source)
|
|
61
|
+
return;
|
|
62
|
+
let sourceMap = this.quoteCache.get(poolAddress);
|
|
63
|
+
if (!sourceMap) {
|
|
64
|
+
sourceMap = new Map();
|
|
65
|
+
this.quoteCache.set(poolAddress, sourceMap);
|
|
66
|
+
}
|
|
67
|
+
const existing = sourceMap.get(source);
|
|
68
|
+
const verifiedReferenceBlock = (existing && existing.referenceBlock === blockNumber)
|
|
69
|
+
? existing.verifiedReferenceBlock
|
|
70
|
+
: 0;
|
|
71
|
+
sourceMap.set(source, {
|
|
72
|
+
priceId, source, askPrice, bidPrice,
|
|
73
|
+
askTiers: tiers?.askTiers,
|
|
74
|
+
bidTiers: tiers?.bidTiers,
|
|
75
|
+
referenceBlock: blockNumber,
|
|
76
|
+
verifiedReferenceBlock,
|
|
77
|
+
quoteAmountUsd,
|
|
78
|
+
token0PriceUsd, token1PriceUsd,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
checkSwap(params) {
|
|
82
|
+
if (!this.enabled)
|
|
83
|
+
return;
|
|
84
|
+
const { poolAddress, blockNumber, txHash, poolInfo, token0Address } = params;
|
|
85
|
+
const sourceMap = this.quoteCache.get(poolAddress);
|
|
86
|
+
if (!sourceMap || sourceMap.size === 0)
|
|
87
|
+
return;
|
|
88
|
+
const swapData = this.deriveSwapDirection(params);
|
|
89
|
+
if (!swapData)
|
|
90
|
+
return;
|
|
91
|
+
const direction = (0, trade_direction_1.resolveTradeDirection)(poolInfo, true);
|
|
92
|
+
const inputIsQuoteToken = swapData.inputTokenAddress.toLowerCase() === direction.quoteToken.address.toLowerCase();
|
|
93
|
+
const isBuy = inputIsQuoteToken;
|
|
94
|
+
const execPriceStr = (0, trade_direction_1.calculateStandardPrice)(swapData.inputAmountUi, swapData.outputAmountUi, isBuy);
|
|
95
|
+
const execPrice = Number(execPriceStr);
|
|
96
|
+
if (execPrice <= 0)
|
|
97
|
+
return;
|
|
98
|
+
const verifiable = [];
|
|
99
|
+
for (const [source, cached] of sourceMap) {
|
|
100
|
+
if (blockNumber <= cached.referenceBlock)
|
|
101
|
+
continue;
|
|
102
|
+
if (cached.verifiedReferenceBlock === cached.referenceBlock)
|
|
103
|
+
continue;
|
|
104
|
+
const tiers = isBuy ? cached.askTiers : cached.bidTiers;
|
|
105
|
+
let refPrice;
|
|
106
|
+
let tierInfo;
|
|
107
|
+
if (tiers && tiers.length > 0) {
|
|
108
|
+
const match = pickMatchingTier(tiers, swapData.inputAmountUi);
|
|
109
|
+
if (!match)
|
|
110
|
+
continue;
|
|
111
|
+
refPrice = match.tier.price;
|
|
112
|
+
tierInfo = {
|
|
113
|
+
matched_pct: parseFloat(match.tier.pct.toFixed(4)),
|
|
114
|
+
mode: match.mode,
|
|
115
|
+
tier_amount_in: match.tier.amount_in,
|
|
116
|
+
lo_pct: match.lo_pct,
|
|
117
|
+
hi_pct: match.hi_pct,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
refPrice = isBuy ? cached.askPrice : cached.bidPrice;
|
|
122
|
+
}
|
|
123
|
+
if (refPrice <= 0)
|
|
124
|
+
continue;
|
|
125
|
+
cached.verifiedReferenceBlock = cached.referenceBlock;
|
|
126
|
+
const diff_bps = (refPrice - execPrice) / refPrice * 10000;
|
|
127
|
+
verifiable.push({ source, cached, refPrice, diff_bps, tierInfo });
|
|
128
|
+
}
|
|
129
|
+
if (verifiable.length === 0)
|
|
130
|
+
return;
|
|
131
|
+
verifiable.sort((a, b) => b.cached.referenceBlock - a.cached.referenceBlock);
|
|
132
|
+
const primary = verifiable[0];
|
|
133
|
+
const sources = {};
|
|
134
|
+
for (const r of verifiable) {
|
|
135
|
+
sources[r.source] = {
|
|
136
|
+
ask: r.cached.askPrice,
|
|
137
|
+
bid: r.cached.bidPrice,
|
|
138
|
+
matched_ref_price: r.refPrice,
|
|
139
|
+
diff_bps: parseFloat(r.diff_bps.toFixed(1)),
|
|
140
|
+
quote_block: r.cached.referenceBlock,
|
|
141
|
+
...(r.tierInfo && {
|
|
142
|
+
matched_pct: r.tierInfo.matched_pct,
|
|
143
|
+
tier_mode: r.tierInfo.mode,
|
|
144
|
+
tier_amount_in: r.tierInfo.tier_amount_in,
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return this.compareAndLog(poolAddress, params.poolName, poolInfo, primary.cached, swapData.inputTokenAddress, swapData.outputTokenAddress, swapData.inputAmountUi, swapData.outputAmountUi, primary.refPrice, execPrice, primary.cached.token0PriceUsd, primary.cached.token1PriceUsd, token0Address, blockNumber, txHash, sources, primary.tierInfo);
|
|
149
|
+
}
|
|
150
|
+
deriveSwapDirection(params) {
|
|
151
|
+
const { amount0, amount1, token0Address, token1Address, token0Decimals, token1Decimals } = params;
|
|
152
|
+
const sign = params.swapperDeltaConvention ? -1n : 1n;
|
|
153
|
+
const amt0 = BigInt(amount0) * sign;
|
|
154
|
+
const amt1 = BigInt(amount1) * sign;
|
|
155
|
+
if (amt0 === 0n && amt1 === 0n)
|
|
156
|
+
return null;
|
|
157
|
+
if (amt0 > 0n && amt1 < 0n) {
|
|
158
|
+
return {
|
|
159
|
+
inputTokenAddress: token0Address,
|
|
160
|
+
outputTokenAddress: token1Address,
|
|
161
|
+
inputAmountUi: Number(amt0) / Math.pow(10, token0Decimals),
|
|
162
|
+
outputAmountUi: Number(-amt1) / Math.pow(10, token1Decimals),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
else if (amt0 < 0n && amt1 > 0n) {
|
|
166
|
+
return {
|
|
167
|
+
inputTokenAddress: token1Address,
|
|
168
|
+
outputTokenAddress: token0Address,
|
|
169
|
+
inputAmountUi: Number(amt1) / Math.pow(10, token1Decimals),
|
|
170
|
+
outputAmountUi: Number(-amt0) / Math.pow(10, token0Decimals),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
compareAndLog(poolAddress, poolName, poolInfo, cached, inputTokenAddress, outputTokenAddress, inputAmountUi, outputAmountUi, refPrice, execPriceNum, token0PriceUsd, token1PriceUsd, token0Address, swapBlockNumber, txHash, sources, primaryTierInfo) {
|
|
176
|
+
if (inputAmountUi <= 0 || outputAmountUi <= 0)
|
|
177
|
+
return;
|
|
178
|
+
let swapUsd = 0;
|
|
179
|
+
if (token0PriceUsd && token1PriceUsd && token0Address) {
|
|
180
|
+
const inputIsToken0 = inputTokenAddress.toLowerCase() === token0Address.toLowerCase();
|
|
181
|
+
swapUsd = inputIsToken0
|
|
182
|
+
? inputAmountUi * token0PriceUsd
|
|
183
|
+
: inputAmountUi * token1PriceUsd;
|
|
184
|
+
}
|
|
185
|
+
const direction = (0, trade_direction_1.resolveTradeDirection)(poolInfo, true);
|
|
186
|
+
const baseToken = direction.baseToken;
|
|
187
|
+
const quoteToken = direction.quoteToken;
|
|
188
|
+
const inputIsQuoteToken = inputTokenAddress.toLowerCase() === quoteToken.address.toLowerCase();
|
|
189
|
+
const isBuy = inputIsQuoteToken;
|
|
190
|
+
const side = isBuy ? 'BUY' : 'SELL';
|
|
191
|
+
if (refPrice <= 0 || execPriceNum <= 0)
|
|
192
|
+
return;
|
|
193
|
+
const diffBps = (refPrice - execPriceNum) / refPrice * 10000;
|
|
194
|
+
const absDiffBps = Math.abs(diffBps);
|
|
195
|
+
const status = absDiffBps < 5 ? '✅' : absDiffBps < 10 ? '⚠️' : '❌';
|
|
196
|
+
const usdStr = swapUsd > 0 ? `$${swapUsd.toFixed(0)}` : '$?';
|
|
197
|
+
const maxUsd = cached.quoteAmountUsd * 2;
|
|
198
|
+
const inRange = swapUsd <= 0 || swapUsd <= maxUsd;
|
|
199
|
+
const rangeTag = inRange ? '' : ' [out]';
|
|
200
|
+
const tradeFlow = isBuy
|
|
201
|
+
? `${inputAmountUi.toFixed(4)} ${quoteToken.symbol} -> ${outputAmountUi.toFixed(4)} ${baseToken.symbol}`
|
|
202
|
+
: `${inputAmountUi.toFixed(4)} ${baseToken.symbol} -> ${outputAmountUi.toFixed(4)} ${quoteToken.symbol}`;
|
|
203
|
+
const quoteSrc = cached.source || '?';
|
|
204
|
+
const quoteTag = `quote[${quoteSrc} blk:${cached.referenceBlock}]`;
|
|
205
|
+
const swapBlk = swapBlockNumber || '?';
|
|
206
|
+
const swapTx = txHash ? ` ${txHash.slice(0, 10)}` : '';
|
|
207
|
+
const swapTag = `swap[blk:${swapBlk}${swapTx}]`;
|
|
208
|
+
const priceLabel = isBuy ? 'ask' : 'bid';
|
|
209
|
+
const msg = ` ↳ [Verify] ${side} ${usdStr} (${tradeFlow}) ${quoteTag} ${priceLabel}=${refPrice.toFixed(12)} vs ${swapTag} exec=${execPriceNum.toFixed(12)} diff=${diffBps > 0 ? '+' : ''}${diffBps.toFixed(1)}bps ${status}${rangeTag}`;
|
|
210
|
+
try {
|
|
211
|
+
(0, ttd_core_1.report_data_to_analyze)('QuoteVerify', {
|
|
212
|
+
pool_address: poolAddress,
|
|
213
|
+
pool_name: poolName,
|
|
214
|
+
price_id: cached.priceId,
|
|
215
|
+
source: cached.source,
|
|
216
|
+
quote_amount_usd: cached.quoteAmountUsd,
|
|
217
|
+
quote_block: cached.referenceBlock,
|
|
218
|
+
swap_block: swapBlockNumber,
|
|
219
|
+
tx_hash: txHash || '',
|
|
220
|
+
side,
|
|
221
|
+
ref_price: refPrice,
|
|
222
|
+
exec_price: execPriceNum,
|
|
223
|
+
diff_bps: parseFloat(diffBps.toFixed(1)),
|
|
224
|
+
swap_usd: parseFloat(swapUsd.toFixed(0)),
|
|
225
|
+
in_range: inRange,
|
|
226
|
+
input_amount: inputAmountUi,
|
|
227
|
+
output_amount: outputAmountUi,
|
|
228
|
+
input_symbol: isBuy ? quoteToken.symbol : baseToken.symbol,
|
|
229
|
+
output_symbol: isBuy ? baseToken.symbol : quoteToken.symbol,
|
|
230
|
+
status,
|
|
231
|
+
time: Date.now(),
|
|
232
|
+
sources: sources || undefined,
|
|
233
|
+
...(primaryTierInfo && {
|
|
234
|
+
matched_tier_pct: primaryTierInfo.matched_pct,
|
|
235
|
+
matched_tier_mode: primaryTierInfo.mode,
|
|
236
|
+
matched_tier_amount_in: primaryTierInfo.tier_amount_in,
|
|
237
|
+
matched_tier_lo_pct: primaryTierInfo.lo_pct,
|
|
238
|
+
matched_tier_hi_pct: primaryTierInfo.hi_pct,
|
|
239
|
+
}),
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
catch (_) {
|
|
243
|
+
}
|
|
244
|
+
return msg;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
exports.QuotePriceVerify = QuotePriceVerify;
|
package/dist/trade/index.d.ts
CHANGED
package/dist/trade/index.js
CHANGED
|
@@ -16,7 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./tx_result_check"), exports);
|
|
18
18
|
__exportStar(require("./tx_result_parse"), exports);
|
|
19
|
-
__exportStar(require("../config/SolanaTradeAppConfig"), exports);
|
|
20
19
|
__exportStar(require("./tx_builder"), exports);
|
|
21
20
|
__exportStar(require("./send"), exports);
|
|
22
21
|
__exportStar(require("./jito_tip_wallets"), exports);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TradeContext } from "@clonegod/ttd-core/dist";
|
|
2
2
|
import { Connection, Keypair, Transaction, TransactionInstruction } from "@solana/web3.js";
|
|
3
|
-
import { SolanaTradeAppConfig } from "../
|
|
3
|
+
import { SolanaTradeAppConfig } from "../appconfig/SolanaTradeAppConfig";
|
|
4
4
|
import { JitoTipWalletManager } from "./jito_tip_wallets";
|
|
5
5
|
export declare class SolTransactionBuilder {
|
|
6
6
|
appConfig: SolanaTradeAppConfig;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export declare enum SolanaPoolAccountType {
|
|
2
2
|
POOL = "pool",
|
|
3
3
|
VAULT_A = "vaultA",
|
|
4
|
-
VAULT_B = "vaultB"
|
|
4
|
+
VAULT_B = "vaultB",
|
|
5
|
+
TICK_ARRAY = "tickArray",
|
|
6
|
+
BIN_ARRAY = "binArray"
|
|
5
7
|
}
|
|
6
8
|
export interface SolanaAccountUpdateEvent {
|
|
7
9
|
recvTimeMs: number;
|
|
@@ -35,6 +37,7 @@ export interface SolanaPoolAccountUpdateEventData {
|
|
|
35
37
|
pool_name: string;
|
|
36
38
|
data: {
|
|
37
39
|
slot: number;
|
|
40
|
+
write_version?: number;
|
|
38
41
|
tx_hash: string;
|
|
39
42
|
pool_account: string;
|
|
40
43
|
pool_account_data: string;
|
|
@@ -42,5 +45,10 @@ export interface SolanaPoolAccountUpdateEventData {
|
|
|
42
45
|
vaultA_account_data?: string;
|
|
43
46
|
vaultB_account?: string;
|
|
44
47
|
vaultB_account_data?: string;
|
|
48
|
+
sub_account?: {
|
|
49
|
+
role: string;
|
|
50
|
+
account: string;
|
|
51
|
+
account_data: string;
|
|
52
|
+
};
|
|
45
53
|
};
|
|
46
54
|
}
|
package/dist/types/index.js
CHANGED
|
@@ -6,4 +6,6 @@ var SolanaPoolAccountType;
|
|
|
6
6
|
SolanaPoolAccountType["POOL"] = "pool";
|
|
7
7
|
SolanaPoolAccountType["VAULT_A"] = "vaultA";
|
|
8
8
|
SolanaPoolAccountType["VAULT_B"] = "vaultB";
|
|
9
|
+
SolanaPoolAccountType["TICK_ARRAY"] = "tickArray";
|
|
10
|
+
SolanaPoolAccountType["BIN_ARRAY"] = "binArray";
|
|
9
11
|
})(SolanaPoolAccountType || (exports.SolanaPoolAccountType = SolanaPoolAccountType = {}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './trade_direction';
|