@whiterresearch/litvmswap-aggregator 1.0.4

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.
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LitVMSwapBuilder = void 0;
4
+ const viem_1 = require("viem");
5
+ const index_js_1 = require("../constants/index.js");
6
+ const index_js_2 = require("../abi/index.js");
7
+ class LitVMSwapBuilder {
8
+ /**
9
+ * Prepares a ready-to-sign transaction for executing a swap
10
+ */
11
+ static buildSwapTransaction(params) {
12
+ const { quote, userAddress, receiverAddress, feeConfig } = params;
13
+ const receiver = receiverAddress ?? userAddress;
14
+ // 1. Handle Wrap / Unwrap directly
15
+ if (quote.route.poolType === 'wrap') {
16
+ const isWrap = quote.tokenIn.isNative;
17
+ const wethAddr = index_js_1.LITVM_ADDRESSES.weth;
18
+ if (isWrap) {
19
+ // Native -> wzkLTC (Deposit)
20
+ const data = (0, viem_1.encodeFunctionData)({
21
+ abi: index_js_2.ERC20ABI,
22
+ functionName: 'deposit',
23
+ });
24
+ return {
25
+ to: wethAddr,
26
+ data,
27
+ value: quote.amountIn,
28
+ chainId: index_js_1.LITECOIN_VM_CHAIN_ID,
29
+ description: `Wrap ${quote.amountInFormatted} ${quote.tokenIn.symbol} to ${quote.tokenOut.symbol}`,
30
+ };
31
+ }
32
+ else {
33
+ // wzkLTC -> Native (Withdraw)
34
+ const data = (0, viem_1.encodeFunctionData)({
35
+ abi: index_js_2.ERC20ABI,
36
+ functionName: 'withdraw',
37
+ args: [quote.amountIn],
38
+ });
39
+ return {
40
+ to: wethAddr,
41
+ data,
42
+ value: 0n,
43
+ chainId: index_js_1.LITECOIN_VM_CHAIN_ID,
44
+ description: `Unwrap ${quote.amountInFormatted} ${quote.tokenIn.symbol} to ${quote.tokenOut.symbol}`,
45
+ };
46
+ }
47
+ }
48
+ // 2. Route through the entrypoint unless the caller explicitly opts out.
49
+ //
50
+ // This used to be conditional on a feeConfig being supplied, so the DEFAULT
51
+ // was a direct router call paying no protocol fee and earning the user no
52
+ // Lit Diamonds. Integrators got that by accident. The fee path is now the
53
+ // default and the bypass is explicit.
54
+ if (!params.useDirectRouter) {
55
+ // The points wrapper, not the bare entrypoint: it records Lit Diamonds and
56
+ // the NFT staking multiplier before forwarding to the fee-enforcing
57
+ // entrypoint. Calling past it silently drops points the user earned.
58
+ const entrypointAddress = index_js_1.LITVM_ADDRESSES.aggregatorEntrypoint;
59
+ const swapIntent = {
60
+ tokenUserBuys: quote.tokenOut.isNative ? viem_1.zeroAddress : quote.tokenOut.address,
61
+ minAmountUserBuys: quote.minAmountOut,
62
+ tokenUserSells: quote.tokenIn.isNative ? viem_1.zeroAddress : quote.tokenIn.address,
63
+ amountUserSells: quote.amountIn,
64
+ };
65
+ // The entrypoint enforces both of these on-chain: the fee must go to the
66
+ // DexFeeVault and must be at least MIN_PROTOCOL_FEE_BPS. Defaulting feeBps
67
+ // to 0n, as this did, produced transactions that revert with FeeBelowMinimum.
68
+ const requestedFeeBps = feeConfig?.feeBps !== undefined ? BigInt(feeConfig.feeBps) : index_js_1.MIN_PROTOCOL_FEE_BPS;
69
+ const feeCollection = {
70
+ feeCollectorAddress: feeConfig?.feeCollectorAddress ?? index_js_1.LITVM_ADDRESSES.platformFeeCollector,
71
+ feeBps: requestedFeeBps < index_js_1.MIN_PROTOCOL_FEE_BPS ? index_js_1.MIN_PROTOCOL_FEE_BPS : requestedFeeBps,
72
+ referrerAddress: feeConfig?.referrerAddress ?? viem_1.zeroAddress,
73
+ referrerFeeBps: feeConfig?.referrerFeeBps ? BigInt(feeConfig.referrerFeeBps) : 0n,
74
+ isInTokenFee: feeConfig?.isInTokenFee ?? true,
75
+ };
76
+ const isDiffReceiver = receiver.toLowerCase() !== userAddress.toLowerCase();
77
+ const data = isDiffReceiver
78
+ ? (0, viem_1.encodeFunctionData)({
79
+ abi: index_js_2.AGGFlowEntrypointABI,
80
+ functionName: 'executeSwapWithReceiver',
81
+ args: [swapIntent, feeCollection, quote.program, receiver],
82
+ })
83
+ : (0, viem_1.encodeFunctionData)({
84
+ abi: index_js_2.AGGFlowEntrypointABI,
85
+ functionName: 'executeSwap',
86
+ args: [swapIntent, feeCollection, quote.program],
87
+ });
88
+ return {
89
+ to: entrypointAddress,
90
+ data,
91
+ value: quote.tokenIn.isNative ? quote.amountIn : 0n,
92
+ chainId: index_js_1.LITECOIN_VM_CHAIN_ID,
93
+ description: `Swap ${quote.amountInFormatted} ${quote.tokenIn.symbol} for ${quote.tokenOut.symbol} via AGGFlowPointsWrapper`,
94
+ };
95
+ }
96
+ // 3. Explicit bypass: straight to AGGFlowRouter.
97
+ // No protocol fee, and no Lit Diamonds for the user. Reached only when the
98
+ // caller sets useDirectRouter: true.
99
+ const routerAddress = index_js_1.LITVM_ADDRESSES.aggregatorRouter;
100
+ const tokenIn = quote.tokenIn.isNative ? viem_1.zeroAddress : quote.tokenIn.address;
101
+ const tokenOut = quote.tokenOut.isNative ? viem_1.zeroAddress : quote.tokenOut.address;
102
+ const data = (0, viem_1.encodeFunctionData)({
103
+ abi: index_js_2.AGGFlowRouterABI,
104
+ functionName: 'executeRoute',
105
+ args: [tokenIn, quote.amountIn, tokenOut, quote.minAmountOut, quote.program],
106
+ });
107
+ return {
108
+ to: routerAddress,
109
+ data,
110
+ value: quote.tokenIn.isNative ? quote.amountIn : 0n,
111
+ chainId: index_js_1.LITECOIN_VM_CHAIN_ID,
112
+ description: `Swap ${quote.amountInFormatted} ${quote.tokenIn.symbol} for ${quote.tokenOut.symbol} via AGGFlowRouter (no protocol fee, no points)`,
113
+ };
114
+ }
115
+ /**
116
+ * Prepares an ERC-20 approval transaction
117
+ * @param tokenAddress The ERC-20 token contract
118
+ * @param spender The contract to approve (Defaults to AGGFlowEntrypoint)
119
+ * @param amount Optional amount to approve (defaults to max uint256)
120
+ */
121
+ static buildApproveTransaction(tokenAddress, spender = index_js_1.LITVM_ADDRESSES.aggregatorEntrypoint, amount = viem_1.maxUint256) {
122
+ const data = (0, viem_1.encodeFunctionData)({
123
+ abi: index_js_2.ERC20ABI,
124
+ functionName: 'approve',
125
+ args: [spender, amount],
126
+ });
127
+ return {
128
+ to: tokenAddress,
129
+ data,
130
+ value: 0n,
131
+ chainId: index_js_1.LITECOIN_VM_CHAIN_ID,
132
+ description: `Approve ${spender} to spend token`,
133
+ };
134
+ }
135
+ }
136
+ exports.LitVMSwapBuilder = LitVMSwapBuilder;
@@ -0,0 +1,80 @@
1
+ import type { Address, Hex } from 'viem';
2
+ import type { TokenInfo } from '../constants/index.js';
3
+ export type PoolType = 'v2' | 'v3' | 'curve' | 'wrap';
4
+ export interface RouteLeg {
5
+ dexName: string;
6
+ poolType: PoolType;
7
+ poolAddress: Address;
8
+ tokenIn: Address;
9
+ tokenOut: Address;
10
+ fee?: number;
11
+ curvePoolType?: number;
12
+ curveIndices?: {
13
+ from: number;
14
+ to: number;
15
+ };
16
+ }
17
+ export interface QuoteParams {
18
+ tokenIn: Address | string;
19
+ tokenOut: Address | string;
20
+ amountIn: bigint | string;
21
+ slippageTolerancePercent?: number;
22
+ }
23
+ export interface QuoteResult {
24
+ tokenIn: TokenInfo;
25
+ tokenOut: TokenInfo;
26
+ amountIn: bigint;
27
+ amountInFormatted: string;
28
+ expectedAmountOut: bigint;
29
+ expectedAmountOutFormatted: string;
30
+ minAmountOut: bigint;
31
+ minAmountOutFormatted: string;
32
+ priceImpactPercent: number;
33
+ executionPrice: number;
34
+ route: RouteLeg;
35
+ program: Hex;
36
+ }
37
+ export interface FeeConfig {
38
+ feeCollectorAddress?: Address;
39
+ feeBps?: bigint | number;
40
+ referrerAddress?: Address;
41
+ referrerFeeBps?: bigint | number;
42
+ isInTokenFee?: boolean;
43
+ }
44
+ export interface BuildSwapTxParams {
45
+ quote: QuoteResult;
46
+ userAddress: Address;
47
+ receiverAddress?: Address;
48
+ feeConfig?: FeeConfig;
49
+ /** @deprecated Swaps route through the entrypoint by default. Use `useDirectRouter` to opt out. */
50
+ useEntrypoint?: boolean;
51
+ /**
52
+ * Bypass the entrypoint and call AGGFlowRouter directly.
53
+ *
54
+ * This pays NO protocol fee and earns the user NO Lit Diamonds or NFT staking
55
+ * multiplier. It used to be the default whenever no feeConfig was supplied,
56
+ * which meant an integrator got it by accident rather than by choice. It is
57
+ * now opt-in, and deliberately verbose.
58
+ */
59
+ useDirectRouter?: boolean;
60
+ }
61
+ export interface PreparedTransaction {
62
+ to: Address;
63
+ data: Hex;
64
+ value: bigint;
65
+ chainId: number;
66
+ description: string;
67
+ }
68
+ export interface SwapIntent {
69
+ tokenUserBuys: Address;
70
+ minAmountUserBuys: bigint;
71
+ tokenUserSells: Address;
72
+ amountUserSells: bigint;
73
+ }
74
+ export interface FeeCollection {
75
+ feeCollectorAddress: Address;
76
+ feeBps: bigint;
77
+ referrerAddress: Address;
78
+ referrerFeeBps: bigint;
79
+ isInTokenFee: boolean;
80
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Parses user input string or bigint to Wei BigInt
3
+ */
4
+ export declare function toBigIntAmount(amount: string | bigint | number, decimals: number): bigint;
5
+ /**
6
+ * Formats a BigInt amount into human readable string
7
+ */
8
+ export declare function formatTokenAmount(amount: bigint, decimals: number, precision?: number): string;
9
+ /**
10
+ * Calculates minimum amount out given a slippage tolerance percentage
11
+ * @param amountOut Expected amount out
12
+ * @param slippagePercent Slippage tolerance (e.g. 0.5 for 0.5%)
13
+ */
14
+ export declare function calculateMinAmountOut(amountOut: bigint, slippagePercent: number): bigint;
15
+ /**
16
+ * Calculates price impact percentage
17
+ */
18
+ export declare function calculatePriceImpact(amountIn: bigint, amountOut: bigint, reserveIn: bigint, reserveOut: bigint): number;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toBigIntAmount = toBigIntAmount;
4
+ exports.formatTokenAmount = formatTokenAmount;
5
+ exports.calculateMinAmountOut = calculateMinAmountOut;
6
+ exports.calculatePriceImpact = calculatePriceImpact;
7
+ const viem_1 = require("viem");
8
+ /**
9
+ * Parses user input string or bigint to Wei BigInt
10
+ */
11
+ function toBigIntAmount(amount, decimals) {
12
+ if (typeof amount === 'bigint') {
13
+ return amount;
14
+ }
15
+ const str = typeof amount === 'number' ? amount.toString() : amount.trim();
16
+ return (0, viem_1.parseUnits)(str, decimals);
17
+ }
18
+ /**
19
+ * Formats a BigInt amount into human readable string
20
+ */
21
+ function formatTokenAmount(amount, decimals, precision = 6) {
22
+ const formatted = (0, viem_1.formatUnits)(amount, decimals);
23
+ const parts = formatted.split('.');
24
+ if (parts.length === 1)
25
+ return parts[0];
26
+ const truncated = parts[1].slice(0, precision);
27
+ return `${parts[0]}.${truncated}`;
28
+ }
29
+ /**
30
+ * Calculates minimum amount out given a slippage tolerance percentage
31
+ * @param amountOut Expected amount out
32
+ * @param slippagePercent Slippage tolerance (e.g. 0.5 for 0.5%)
33
+ */
34
+ function calculateMinAmountOut(amountOut, slippagePercent) {
35
+ const basisPoints = BigInt(Math.floor(slippagePercent * 100));
36
+ const minAmount = (amountOut * (10000n - basisPoints)) / 10000n;
37
+ return minAmount > 0n ? minAmount : 0n;
38
+ }
39
+ /**
40
+ * Calculates price impact percentage
41
+ */
42
+ function calculatePriceImpact(amountIn, amountOut, reserveIn, reserveOut) {
43
+ if (reserveIn === 0n || reserveOut === 0n)
44
+ return 0;
45
+ // Mid price = reserveOut / reserveIn
46
+ // Execution price = amountOut / amountIn
47
+ const midPrice = Number(reserveOut) / Number(reserveIn);
48
+ const execPrice = Number(amountOut) / Number(amountIn);
49
+ const impact = ((midPrice - execPrice) / midPrice) * 100;
50
+ return Math.max(0, parseFloat(impact.toFixed(2)));
51
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@whiterresearch/litvmswap-aggregator",
3
+ "version": "1.0.4",
4
+ "description": "Official TypeScript/JavaScript SDK for the LitecoinVM (LitVM) DEX Aggregator",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./constants": {
13
+ "types": "./dist/constants/index.d.ts",
14
+ "default": "./dist/constants/index.js"
15
+ },
16
+ "./abi": {
17
+ "types": "./dist/abi/index.d.ts",
18
+ "default": "./dist/abi/index.js"
19
+ },
20
+ "./package.json": "./package.json"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "prepublishOnly": "tsc",
30
+ "typecheck": "tsc --noEmit"
31
+ },
32
+ "keywords": [
33
+ "litvm",
34
+ "litecoinvm",
35
+ "dex",
36
+ "aggregator",
37
+ "sdk",
38
+ "defi",
39
+ "swap",
40
+ "uniswap",
41
+ "ethereum"
42
+ ],
43
+ "author": "LitVM Ecosystem",
44
+ "license": "MIT",
45
+ "dependencies": {
46
+ "viem": "^2.21.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "typescript": "^5.9.3"
51
+ },
52
+ "homepage": "https://litvmswap.com",
53
+ "publishConfig": {
54
+ "access": "public"
55
+ }
56
+ }