@mento-protocol/mento-sdk 3.1.0-beta.5 → 3.1.0-beta.6
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/core/abis/index.d.ts +1 -0
- package/dist/core/abis/index.js +1 -0
- package/dist/core/abis/liquidityStrategy.d.ts +132 -0
- package/dist/core/abis/liquidityStrategy.js +10 -0
- package/dist/core/constants/addresses.js +1 -0
- package/dist/core/types/contractAddresses.d.ts +1 -0
- package/dist/core/types/liquidity.d.ts +23 -0
- package/dist/core/types/pool.d.ts +64 -1
- package/dist/esm/core/abis/index.js +1 -0
- package/dist/esm/core/abis/liquidityStrategy.js +6 -0
- package/dist/esm/core/constants/addresses.js +1 -0
- package/dist/esm/services/liquidity/LiquidityService.js +18 -0
- package/dist/esm/services/liquidity/liquidityHelpers.js +9 -6
- package/dist/esm/services/liquidity/rebalance.js +59 -0
- package/dist/esm/services/pools/PoolService.js +9 -0
- package/dist/esm/services/pools/poolDetails.js +35 -35
- package/dist/esm/services/pools/rebalancePreview.js +181 -0
- package/dist/services/liquidity/LiquidityService.d.ts +16 -1
- package/dist/services/liquidity/LiquidityService.js +18 -0
- package/dist/services/liquidity/liquidityHelpers.d.ts +2 -2
- package/dist/services/liquidity/liquidityHelpers.js +9 -6
- package/dist/services/liquidity/rebalance.d.ts +6 -0
- package/dist/services/liquidity/rebalance.js +64 -0
- package/dist/services/pools/PoolService.d.ts +3 -1
- package/dist/services/pools/PoolService.js +9 -0
- package/dist/services/pools/poolDetails.js +34 -34
- package/dist/services/pools/rebalancePreview.d.ts +5 -0
- package/dist/services/pools/rebalancePreview.js +186 -0
- package/package.json +1 -1
package/dist/core/abis/index.js
CHANGED
|
@@ -34,4 +34,5 @@ __exportStar(require("./troveNFT"), exports);
|
|
|
34
34
|
__exportStar(require("./priceFeed"), exports);
|
|
35
35
|
__exportStar(require("./addressesRegistry"), exports);
|
|
36
36
|
__exportStar(require("./systemParams"), exports);
|
|
37
|
+
__exportStar(require("./liquidityStrategy"), exports);
|
|
37
38
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export declare const LIQUIDITY_STRATEGY_ABI: readonly [{
|
|
2
|
+
readonly name: "poolConfigs";
|
|
3
|
+
readonly type: "function";
|
|
4
|
+
readonly stateMutability: "view";
|
|
5
|
+
readonly inputs: readonly [{
|
|
6
|
+
readonly type: "address";
|
|
7
|
+
}];
|
|
8
|
+
readonly outputs: readonly [{
|
|
9
|
+
readonly type: "bool";
|
|
10
|
+
readonly name: "isToken0Debt";
|
|
11
|
+
}, {
|
|
12
|
+
readonly type: "uint32";
|
|
13
|
+
readonly name: "lastRebalance";
|
|
14
|
+
}, {
|
|
15
|
+
readonly type: "uint32";
|
|
16
|
+
readonly name: "rebalanceCooldown";
|
|
17
|
+
}, {
|
|
18
|
+
readonly type: "address";
|
|
19
|
+
readonly name: "protocolFeeRecipient";
|
|
20
|
+
}, {
|
|
21
|
+
readonly type: "uint64";
|
|
22
|
+
readonly name: "liquiditySourceIncentiveExpansion";
|
|
23
|
+
}, {
|
|
24
|
+
readonly type: "uint64";
|
|
25
|
+
readonly name: "protocolIncentiveExpansion";
|
|
26
|
+
}, {
|
|
27
|
+
readonly type: "uint64";
|
|
28
|
+
readonly name: "liquiditySourceIncentiveContraction";
|
|
29
|
+
}, {
|
|
30
|
+
readonly type: "uint64";
|
|
31
|
+
readonly name: "protocolIncentiveContraction";
|
|
32
|
+
}];
|
|
33
|
+
}, {
|
|
34
|
+
readonly name: "determineAction";
|
|
35
|
+
readonly type: "function";
|
|
36
|
+
readonly stateMutability: "view";
|
|
37
|
+
readonly inputs: readonly [{
|
|
38
|
+
readonly type: "address";
|
|
39
|
+
readonly name: "pool";
|
|
40
|
+
}];
|
|
41
|
+
readonly outputs: readonly [{
|
|
42
|
+
readonly type: "tuple";
|
|
43
|
+
readonly components: readonly [{
|
|
44
|
+
readonly type: "address";
|
|
45
|
+
readonly name: "pool";
|
|
46
|
+
}, {
|
|
47
|
+
readonly type: "tuple";
|
|
48
|
+
readonly components: readonly [{
|
|
49
|
+
readonly type: "uint256";
|
|
50
|
+
readonly name: "reserveNum";
|
|
51
|
+
}, {
|
|
52
|
+
readonly type: "uint256";
|
|
53
|
+
readonly name: "reserveDen";
|
|
54
|
+
}];
|
|
55
|
+
readonly name: "reserves";
|
|
56
|
+
}, {
|
|
57
|
+
readonly type: "tuple";
|
|
58
|
+
readonly components: readonly [{
|
|
59
|
+
readonly type: "uint256";
|
|
60
|
+
readonly name: "oracleNum";
|
|
61
|
+
}, {
|
|
62
|
+
readonly type: "uint256";
|
|
63
|
+
readonly name: "oracleDen";
|
|
64
|
+
}, {
|
|
65
|
+
readonly type: "bool";
|
|
66
|
+
readonly name: "poolPriceAbove";
|
|
67
|
+
}, {
|
|
68
|
+
readonly type: "uint16";
|
|
69
|
+
readonly name: "rebalanceThreshold";
|
|
70
|
+
}];
|
|
71
|
+
readonly name: "prices";
|
|
72
|
+
}, {
|
|
73
|
+
readonly type: "address";
|
|
74
|
+
readonly name: "token0";
|
|
75
|
+
}, {
|
|
76
|
+
readonly type: "address";
|
|
77
|
+
readonly name: "token1";
|
|
78
|
+
}, {
|
|
79
|
+
readonly type: "uint64";
|
|
80
|
+
readonly name: "token0Dec";
|
|
81
|
+
}, {
|
|
82
|
+
readonly type: "uint64";
|
|
83
|
+
readonly name: "token1Dec";
|
|
84
|
+
}, {
|
|
85
|
+
readonly type: "bool";
|
|
86
|
+
readonly name: "isToken0Debt";
|
|
87
|
+
}, {
|
|
88
|
+
readonly type: "tuple";
|
|
89
|
+
readonly components: readonly [{
|
|
90
|
+
readonly type: "uint64";
|
|
91
|
+
readonly name: "liquiditySourceIncentiveExpansion";
|
|
92
|
+
}, {
|
|
93
|
+
readonly type: "uint64";
|
|
94
|
+
readonly name: "protocolIncentiveExpansion";
|
|
95
|
+
}, {
|
|
96
|
+
readonly type: "uint64";
|
|
97
|
+
readonly name: "liquiditySourceIncentiveContraction";
|
|
98
|
+
}, {
|
|
99
|
+
readonly type: "uint64";
|
|
100
|
+
readonly name: "protocolIncentiveContraction";
|
|
101
|
+
}];
|
|
102
|
+
readonly name: "incentives";
|
|
103
|
+
}];
|
|
104
|
+
readonly name: "ctx";
|
|
105
|
+
}, {
|
|
106
|
+
readonly type: "tuple";
|
|
107
|
+
readonly components: readonly [{
|
|
108
|
+
readonly type: "uint8";
|
|
109
|
+
readonly name: "dir";
|
|
110
|
+
}, {
|
|
111
|
+
readonly type: "uint256";
|
|
112
|
+
readonly name: "amount0Out";
|
|
113
|
+
}, {
|
|
114
|
+
readonly type: "uint256";
|
|
115
|
+
readonly name: "amount1Out";
|
|
116
|
+
}, {
|
|
117
|
+
readonly type: "uint256";
|
|
118
|
+
readonly name: "amountOwedToPool";
|
|
119
|
+
}];
|
|
120
|
+
readonly name: "action";
|
|
121
|
+
}];
|
|
122
|
+
}, {
|
|
123
|
+
readonly name: "rebalance";
|
|
124
|
+
readonly type: "function";
|
|
125
|
+
readonly stateMutability: "nonpayable";
|
|
126
|
+
readonly inputs: readonly [{
|
|
127
|
+
readonly type: "address";
|
|
128
|
+
readonly name: "pool";
|
|
129
|
+
}];
|
|
130
|
+
readonly outputs: readonly [];
|
|
131
|
+
}];
|
|
132
|
+
//# sourceMappingURL=liquidityStrategy.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LIQUIDITY_STRATEGY_ABI = void 0;
|
|
4
|
+
const viem_1 = require("viem");
|
|
5
|
+
exports.LIQUIDITY_STRATEGY_ABI = (0, viem_1.parseAbi)([
|
|
6
|
+
'function poolConfigs(address) view returns (bool isToken0Debt, uint32 lastRebalance, uint32 rebalanceCooldown, address protocolFeeRecipient, uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction)',
|
|
7
|
+
'function determineAction(address pool) view returns ((address pool, (uint256 reserveNum, uint256 reserveDen) reserves, (uint256 oracleNum, uint256 oracleDen, bool poolPriceAbove, uint16 rebalanceThreshold) prices, address token0, address token1, uint64 token0Dec, uint64 token1Dec, bool isToken0Debt, (uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction) incentives) ctx, (uint8 dir, uint256 amount0Out, uint256 amount1Out, uint256 amountOwedToPool) action)',
|
|
8
|
+
'function rebalance(address pool)',
|
|
9
|
+
]);
|
|
10
|
+
//# sourceMappingURL=liquidityStrategy.js.map
|
|
@@ -44,6 +44,7 @@ exports.addresses = {
|
|
|
44
44
|
FPMMFactory: '0x353ED52bF8482027C0e0b9e3c0e5d96A9F680980',
|
|
45
45
|
Router: '0xcf6cD45210b3ffE3cA28379C4683F1e60D0C2CCd',
|
|
46
46
|
ReserveLiquidityStrategy: '0x734bb3251Ec3f1A83f8f2A8609bcEF649D54EbF8',
|
|
47
|
+
OpenLiquidityStrategy: '0xCCd2aD0603a08EBc14D223a983171ef18192e8c9',
|
|
47
48
|
},
|
|
48
49
|
[chainId_1.ChainId.MONAD]: {},
|
|
49
50
|
[chainId_1.ChainId.CELO_SEPOLIA]: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CallParams } from './transaction';
|
|
2
|
+
import { LiquidityStrategyDirection } from './pool';
|
|
2
3
|
import { RouterRoute } from '../../utils/pathEncoder';
|
|
3
4
|
export interface LiquidityOptions {
|
|
4
5
|
slippageTolerance: number;
|
|
@@ -114,6 +115,25 @@ export interface ZapOutTransaction {
|
|
|
114
115
|
approval: TokenApproval | null;
|
|
115
116
|
zapOut: ZapOutDetails;
|
|
116
117
|
}
|
|
118
|
+
export interface RebalanceDetails {
|
|
119
|
+
params: CallParams;
|
|
120
|
+
poolAddress: string;
|
|
121
|
+
strategyAddress: string;
|
|
122
|
+
inputToken: string;
|
|
123
|
+
outputToken: string;
|
|
124
|
+
amountRequired: bigint;
|
|
125
|
+
expectedAmountTransferred: bigint;
|
|
126
|
+
expectedProtocolIncentive: bigint;
|
|
127
|
+
expectedLiquiditySourceIncentive: bigint;
|
|
128
|
+
approvalToken: string;
|
|
129
|
+
approvalSpender: string;
|
|
130
|
+
approvalAmount: bigint;
|
|
131
|
+
direction: LiquidityStrategyDirection;
|
|
132
|
+
}
|
|
133
|
+
export interface RebalanceTransaction {
|
|
134
|
+
approval: TokenApproval | null;
|
|
135
|
+
rebalance: RebalanceDetails;
|
|
136
|
+
}
|
|
117
137
|
export interface PreparedZapIn {
|
|
118
138
|
routesA: RouterRoute[];
|
|
119
139
|
routesB: RouterRoute[];
|
|
@@ -168,4 +188,7 @@ export interface ZapOutInput {
|
|
|
168
188
|
export interface PrepareZapOutInput extends ZapOutInput {
|
|
169
189
|
owner?: string;
|
|
170
190
|
}
|
|
191
|
+
export interface RebalanceInput {
|
|
192
|
+
poolAddress: string;
|
|
193
|
+
}
|
|
171
194
|
//# sourceMappingURL=liquidity.d.ts.map
|
|
@@ -92,9 +92,72 @@ export interface FPMMRebalancing {
|
|
|
92
92
|
rebalanceThresholdBelowPercent: number;
|
|
93
93
|
/** Whether the current price is within rebalancing thresholds (null when pricing unavailable) */
|
|
94
94
|
inBand: boolean | null;
|
|
95
|
-
/** The
|
|
95
|
+
/** The registered Open Liquidity Strategy address for this pool, or null if none */
|
|
96
96
|
liquidityStrategy: string | null;
|
|
97
97
|
}
|
|
98
|
+
export type LiquidityStrategyDirection = 'Expand' | 'Contract';
|
|
99
|
+
export interface LiquidityStrategyRebalanceIncentives {
|
|
100
|
+
liquiditySourceIncentiveExpansion: bigint;
|
|
101
|
+
protocolIncentiveExpansion: bigint;
|
|
102
|
+
liquiditySourceIncentiveContraction: bigint;
|
|
103
|
+
protocolIncentiveContraction: bigint;
|
|
104
|
+
}
|
|
105
|
+
export interface LiquidityStrategyPoolConfig {
|
|
106
|
+
isToken0Debt: boolean;
|
|
107
|
+
lastRebalance: number;
|
|
108
|
+
rebalanceCooldown: number;
|
|
109
|
+
protocolFeeRecipient: string;
|
|
110
|
+
liquiditySourceIncentiveExpansion: bigint;
|
|
111
|
+
protocolIncentiveExpansion: bigint;
|
|
112
|
+
liquiditySourceIncentiveContraction: bigint;
|
|
113
|
+
protocolIncentiveContraction: bigint;
|
|
114
|
+
}
|
|
115
|
+
export interface LiquidityStrategyContext {
|
|
116
|
+
pool: string;
|
|
117
|
+
reserves: {
|
|
118
|
+
reserveNum: bigint;
|
|
119
|
+
reserveDen: bigint;
|
|
120
|
+
};
|
|
121
|
+
prices: {
|
|
122
|
+
oracleNum: bigint;
|
|
123
|
+
oracleDen: bigint;
|
|
124
|
+
poolPriceAbove: boolean;
|
|
125
|
+
rebalanceThreshold: number;
|
|
126
|
+
};
|
|
127
|
+
token0: string;
|
|
128
|
+
token1: string;
|
|
129
|
+
token0Dec: bigint;
|
|
130
|
+
token1Dec: bigint;
|
|
131
|
+
isToken0Debt: boolean;
|
|
132
|
+
incentives: LiquidityStrategyRebalanceIncentives;
|
|
133
|
+
}
|
|
134
|
+
export interface LiquidityStrategyAction {
|
|
135
|
+
dir: LiquidityStrategyDirection;
|
|
136
|
+
amount0Out: bigint;
|
|
137
|
+
amount1Out: bigint;
|
|
138
|
+
amountOwedToPool: bigint;
|
|
139
|
+
}
|
|
140
|
+
export interface PoolRebalanceTokenAmount {
|
|
141
|
+
token: string;
|
|
142
|
+
amount: bigint;
|
|
143
|
+
}
|
|
144
|
+
export interface PoolRebalancePreview {
|
|
145
|
+
poolAddress: string;
|
|
146
|
+
strategyAddress: string;
|
|
147
|
+
direction: LiquidityStrategyDirection;
|
|
148
|
+
config: LiquidityStrategyPoolConfig;
|
|
149
|
+
context: LiquidityStrategyContext;
|
|
150
|
+
action: LiquidityStrategyAction;
|
|
151
|
+
inputToken: string;
|
|
152
|
+
outputToken: string;
|
|
153
|
+
amountRequired: PoolRebalanceTokenAmount;
|
|
154
|
+
amountTransferred: PoolRebalanceTokenAmount;
|
|
155
|
+
protocolIncentive: PoolRebalanceTokenAmount;
|
|
156
|
+
liquiditySourceIncentive: PoolRebalanceTokenAmount;
|
|
157
|
+
approvalToken: string;
|
|
158
|
+
approvalSpender: string;
|
|
159
|
+
approvalAmount: bigint;
|
|
160
|
+
}
|
|
98
161
|
/**
|
|
99
162
|
* Enriched details for an FPMM pool including pricing, fees, and rebalancing state
|
|
100
163
|
*/
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { parseAbi } from 'viem';
|
|
2
|
+
export const LIQUIDITY_STRATEGY_ABI = parseAbi([
|
|
3
|
+
'function poolConfigs(address) view returns (bool isToken0Debt, uint32 lastRebalance, uint32 rebalanceCooldown, address protocolFeeRecipient, uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction)',
|
|
4
|
+
'function determineAction(address pool) view returns ((address pool, (uint256 reserveNum, uint256 reserveDen) reserves, (uint256 oracleNum, uint256 oracleDen, bool poolPriceAbove, uint16 rebalanceThreshold) prices, address token0, address token1, uint64 token0Dec, uint64 token1Dec, bool isToken0Debt, (uint64 liquiditySourceIncentiveExpansion, uint64 protocolIncentiveExpansion, uint64 liquiditySourceIncentiveContraction, uint64 protocolIncentiveContraction) incentives) ctx, (uint8 dir, uint256 amount0Out, uint256 amount1Out, uint256 amountOwedToPool) action)',
|
|
5
|
+
'function rebalance(address pool)',
|
|
6
|
+
]);
|
|
@@ -39,6 +39,7 @@ export const addresses = {
|
|
|
39
39
|
FPMMFactory: '0x353ED52bF8482027C0e0b9e3c0e5d96A9F680980',
|
|
40
40
|
Router: '0xcf6cD45210b3ffE3cA28379C4683F1e60D0C2CCd',
|
|
41
41
|
ReserveLiquidityStrategy: '0x734bb3251Ec3f1A83f8f2A8609bcEF649D54EbF8',
|
|
42
|
+
OpenLiquidityStrategy: '0xCCd2aD0603a08EBc14D223a983171ef18192e8c9',
|
|
42
43
|
},
|
|
43
44
|
[ChainId.MONAD]: {},
|
|
44
45
|
[ChainId.CELO_SEPOLIA]: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildAddLiquidityTransactionInternal, buildAddLiquidityParamsInternal, buildRemoveLiquidityTransactionInternal, buildRemoveLiquidityParamsInternal, quoteAddLiquidityInternal, quoteRemoveLiquidityInternal, getLPTokenBalanceInternal, } from './basicLiquidity';
|
|
2
2
|
import { buildZapInTransactionInternal, buildZapInParamsInternal, prepareZapInInternal, quoteZapInInternal, } from './zapIn';
|
|
3
3
|
import { buildZapOutTransactionInternal, buildZapOutParamsInternal, prepareZapOutInternal, quoteZapOutInternal, } from './zapOut';
|
|
4
|
+
import { buildRebalanceParamsInternal, buildRebalanceTransactionInternal, } from './rebalance';
|
|
4
5
|
export class LiquidityService {
|
|
5
6
|
constructor(publicClient, chainId, poolService, routeService) {
|
|
6
7
|
this.publicClient = publicClient;
|
|
@@ -142,4 +143,21 @@ export class LiquidityService {
|
|
|
142
143
|
async quoteZapOut(poolAddress, tokenOut, liquidity, options) {
|
|
143
144
|
return quoteZapOutInternal(this.publicClient, this.chainId, this.poolService, this.routeService, poolAddress, tokenOut, liquidity, options);
|
|
144
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Builds rebalance transaction parameters without checking approval.
|
|
148
|
+
* Use buildRebalanceTransaction if you need approval handling.
|
|
149
|
+
* @param input - Rebalance parameters
|
|
150
|
+
* @returns Transaction details with encoded call data
|
|
151
|
+
*/
|
|
152
|
+
async buildRebalanceParams(input) {
|
|
153
|
+
return buildRebalanceParamsInternal(this.publicClient, this.chainId, this.poolService, input.poolAddress);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Builds a rebalance transaction with ERC20 approval if needed.
|
|
157
|
+
* @param input - Rebalance parameters including owner for allowance checks
|
|
158
|
+
* @returns Transaction with approval (if needed) and rebalance call
|
|
159
|
+
*/
|
|
160
|
+
async buildRebalanceTransaction(input) {
|
|
161
|
+
return buildRebalanceTransactionInternal(this.publicClient, this.chainId, this.poolService, input.poolAddress, input.owner);
|
|
162
|
+
}
|
|
145
163
|
}
|
|
@@ -4,22 +4,25 @@ import { ERC20_ABI, FPMM_ABI } from '../../core/abis';
|
|
|
4
4
|
import { getContractAddress } from '../../core/constants';
|
|
5
5
|
import { validateAddress } from '../../utils/validation';
|
|
6
6
|
import { multicall } from '../../utils/multicall';
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
function getApprovalSpender(chainId, spender) {
|
|
8
|
+
return spender ?? getContractAddress(chainId, 'Router');
|
|
9
|
+
}
|
|
10
|
+
export function buildApprovalParams(chainId, token, amount, spender) {
|
|
11
|
+
const approvalSpender = getApprovalSpender(chainId, spender);
|
|
9
12
|
const data = encodeFunctionData({
|
|
10
13
|
abi: ERC20_ABI,
|
|
11
14
|
functionName: 'approve',
|
|
12
|
-
args: [
|
|
15
|
+
args: [approvalSpender, amount],
|
|
13
16
|
});
|
|
14
17
|
return { to: token, data, value: '0' };
|
|
15
18
|
}
|
|
16
|
-
export async function getAllowance(publicClient, token, owner, chainId) {
|
|
17
|
-
const
|
|
19
|
+
export async function getAllowance(publicClient, token, owner, chainId, spender) {
|
|
20
|
+
const approvalSpender = getApprovalSpender(chainId, spender);
|
|
18
21
|
return (await publicClient.readContract({
|
|
19
22
|
address: token,
|
|
20
23
|
abi: ERC20_ABI,
|
|
21
24
|
functionName: 'allowance',
|
|
22
|
-
args: [owner,
|
|
25
|
+
args: [owner, approvalSpender],
|
|
23
26
|
}));
|
|
24
27
|
}
|
|
25
28
|
export function calculateMinAmount(amount, slippageTolerance) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { encodeFunctionData } from 'viem';
|
|
2
|
+
import { LIQUIDITY_STRATEGY_ABI } from '../../core/abis';
|
|
3
|
+
import { validateAddress } from '../../utils/validation';
|
|
4
|
+
import { buildApprovalParams, getAllowance } from './liquidityHelpers';
|
|
5
|
+
function getRebalanceUnavailableError(poolAddress) {
|
|
6
|
+
return new Error(`Pool ${poolAddress} is not currently rebalanceable or does not have a supported liquidity strategy.`);
|
|
7
|
+
}
|
|
8
|
+
function assertRebalanceActionAmounts(poolAddress, amountRequired, amountTransferred) {
|
|
9
|
+
if (amountRequired <= 0n || amountTransferred <= 0n) {
|
|
10
|
+
throw new Error(`Rebalance action for pool ${poolAddress} has zero amounts and cannot be executed.`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function buildRebalanceParamsInternal(publicClient, chainId, poolService, poolAddress) {
|
|
14
|
+
validateAddress(poolAddress, 'poolAddress');
|
|
15
|
+
const preview = await poolService.getPoolRebalancePreview(poolAddress);
|
|
16
|
+
if (!preview) {
|
|
17
|
+
throw getRebalanceUnavailableError(poolAddress);
|
|
18
|
+
}
|
|
19
|
+
assertRebalanceActionAmounts(poolAddress, preview.amountRequired.amount, preview.amountTransferred.amount);
|
|
20
|
+
const data = encodeFunctionData({
|
|
21
|
+
abi: LIQUIDITY_STRATEGY_ABI,
|
|
22
|
+
functionName: 'rebalance',
|
|
23
|
+
args: [poolAddress],
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
params: {
|
|
27
|
+
to: preview.strategyAddress,
|
|
28
|
+
data,
|
|
29
|
+
value: '0',
|
|
30
|
+
},
|
|
31
|
+
poolAddress,
|
|
32
|
+
strategyAddress: preview.strategyAddress,
|
|
33
|
+
inputToken: preview.inputToken,
|
|
34
|
+
outputToken: preview.outputToken,
|
|
35
|
+
amountRequired: preview.amountRequired.amount,
|
|
36
|
+
expectedAmountTransferred: preview.amountTransferred.amount,
|
|
37
|
+
expectedProtocolIncentive: preview.protocolIncentive.amount,
|
|
38
|
+
expectedLiquiditySourceIncentive: preview.liquiditySourceIncentive.amount,
|
|
39
|
+
approvalToken: preview.approvalToken,
|
|
40
|
+
approvalSpender: preview.approvalSpender,
|
|
41
|
+
approvalAmount: preview.approvalAmount,
|
|
42
|
+
direction: preview.direction,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export async function buildRebalanceTransactionInternal(publicClient, chainId, poolService, poolAddress, owner) {
|
|
46
|
+
validateAddress(owner, 'owner');
|
|
47
|
+
const rebalance = await buildRebalanceParamsInternal(publicClient, chainId, poolService, poolAddress);
|
|
48
|
+
const approvalToken = rebalance.approvalToken;
|
|
49
|
+
const approvalSpender = rebalance.approvalSpender;
|
|
50
|
+
const currentAllowance = await getAllowance(publicClient, approvalToken, owner, chainId, approvalSpender);
|
|
51
|
+
const approval = currentAllowance < rebalance.approvalAmount
|
|
52
|
+
? {
|
|
53
|
+
token: rebalance.approvalToken,
|
|
54
|
+
amount: rebalance.approvalAmount,
|
|
55
|
+
params: buildApprovalParams(chainId, approvalToken, rebalance.approvalAmount, approvalSpender),
|
|
56
|
+
}
|
|
57
|
+
: null;
|
|
58
|
+
return { approval, rebalance };
|
|
59
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PoolType } from '../../core/types';
|
|
2
2
|
import { fetchFPMMPools, fetchVirtualPools } from './poolDiscovery';
|
|
3
3
|
import { fetchFPMMPoolDetailsBatch, fetchVirtualPoolDetailsBatch, } from './poolDetails';
|
|
4
|
+
import { fetchPoolRebalancePreview, fetchPoolRebalancePreviewBatch, } from './rebalancePreview';
|
|
4
5
|
/**
|
|
5
6
|
* Service for discovering liquidity pools in the Mento protocol.
|
|
6
7
|
* Aggregates pools from multiple factory contracts (FPMM and VirtualPool).
|
|
@@ -104,6 +105,14 @@ export class PoolService {
|
|
|
104
105
|
const [details] = await this.getPoolDetailsBatch([poolAddr]);
|
|
105
106
|
return details;
|
|
106
107
|
}
|
|
108
|
+
async getPoolRebalancePreview(poolAddr) {
|
|
109
|
+
const details = await this.getPoolDetails(poolAddr);
|
|
110
|
+
return fetchPoolRebalancePreview(this.publicClient, details);
|
|
111
|
+
}
|
|
112
|
+
async getPoolRebalancePreviewBatch(poolAddresses) {
|
|
113
|
+
const details = await this.getPoolDetailsBatch(poolAddresses);
|
|
114
|
+
return fetchPoolRebalancePreviewBatch(this.publicClient, details);
|
|
115
|
+
}
|
|
107
116
|
async getPoolDetailsBatch(poolAddresses) {
|
|
108
117
|
const pools = await this.getPools();
|
|
109
118
|
const targets = poolAddresses
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { tryGetContractAddress } from '../../core/constants';
|
|
2
2
|
import { FPMM_ABI, VIRTUAL_POOL_ABI } from '../../core/abis';
|
|
3
3
|
import { getAddress } from 'viem';
|
|
4
4
|
import { multicall } from '../../utils/multicall';
|
|
@@ -15,17 +15,18 @@ export async function fetchFPMMPoolDetailsBatch(publicClient, chainId, pools) {
|
|
|
15
15
|
if (pools.length === 0) {
|
|
16
16
|
return [];
|
|
17
17
|
}
|
|
18
|
-
const
|
|
19
|
-
const contracts = pools.flatMap((pool) => buildFPMMContracts(pool,
|
|
18
|
+
const openLiquidityStrategy = getOpenLiquidityStrategy(chainId);
|
|
19
|
+
const contracts = pools.flatMap((pool) => buildFPMMContracts(pool, openLiquidityStrategy));
|
|
20
20
|
const results = await multicall(publicClient, contracts);
|
|
21
|
-
const
|
|
21
|
+
const strategyCheckCount = openLiquidityStrategy ? 1 : 0;
|
|
22
|
+
const perPoolResultCount = FPMM_FIXED_RESULT_COUNT + strategyCheckCount + 1;
|
|
22
23
|
return pools.map((pool, index) => {
|
|
23
24
|
const offset = index * perPoolResultCount;
|
|
24
25
|
const poolResults = results.slice(offset, offset + perPoolResultCount);
|
|
25
|
-
return parseFPMMPoolDetails(pool,
|
|
26
|
+
return parseFPMMPoolDetails(pool, openLiquidityStrategy, poolResults);
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
|
-
function buildFPMMContracts(pool,
|
|
29
|
+
function buildFPMMContracts(pool, openLiquidityStrategy) {
|
|
29
30
|
const address = pool.poolAddr;
|
|
30
31
|
return [
|
|
31
32
|
{ address, abi: FPMM_ABI, functionName: 'getReserves' },
|
|
@@ -36,16 +37,18 @@ function buildFPMMContracts(pool, knownStrategies) {
|
|
|
36
37
|
{ address, abi: FPMM_ABI, functionName: 'rebalanceIncentive' },
|
|
37
38
|
{ address, abi: FPMM_ABI, functionName: 'rebalanceThresholdAbove' },
|
|
38
39
|
{ address, abi: FPMM_ABI, functionName: 'rebalanceThresholdBelow' },
|
|
39
|
-
...
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
...(openLiquidityStrategy
|
|
41
|
+
? [{
|
|
42
|
+
address,
|
|
43
|
+
abi: FPMM_ABI,
|
|
44
|
+
functionName: 'liquidityStrategy',
|
|
45
|
+
args: [openLiquidityStrategy],
|
|
46
|
+
}]
|
|
47
|
+
: []),
|
|
45
48
|
{ address, abi: FPMM_ABI, functionName: 'getRebalancingState' },
|
|
46
49
|
];
|
|
47
50
|
}
|
|
48
|
-
function parseFPMMPoolDetails(pool,
|
|
51
|
+
function parseFPMMPoolDetails(pool, openLiquidityStrategy, results) {
|
|
49
52
|
try {
|
|
50
53
|
const reservesRes = results[0];
|
|
51
54
|
const decimals0Res = results[1];
|
|
@@ -79,10 +82,14 @@ function parseFPMMPoolDetails(pool, knownStrategies, results) {
|
|
|
79
82
|
const rebalanceIncentiveBps = rebalanceIncentiveRes.result;
|
|
80
83
|
const thresholdAboveBps = thresholdAboveRes.result;
|
|
81
84
|
const thresholdBelowBps = thresholdBelowRes.result;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const liquidityStrategy =
|
|
85
|
-
|
|
85
|
+
const strategyCheckCount = openLiquidityStrategy ? 1 : 0;
|
|
86
|
+
const openStrategyResult = strategyCheckCount > 0 ? results[FPMM_FIXED_RESULT_COUNT] : null;
|
|
87
|
+
const liquidityStrategy = openLiquidityStrategy &&
|
|
88
|
+
openStrategyResult?.status === 'success' &&
|
|
89
|
+
openStrategyResult.result === true
|
|
90
|
+
? openLiquidityStrategy
|
|
91
|
+
: null;
|
|
92
|
+
const rebalancingRes = results[FPMM_FIXED_RESULT_COUNT + strategyCheckCount];
|
|
86
93
|
let pricing = null;
|
|
87
94
|
let inBand = null;
|
|
88
95
|
if (rebalancingRes?.status === 'success') {
|
|
@@ -187,23 +194,16 @@ function parseVirtualPoolDetails(pool, results) {
|
|
|
187
194
|
}
|
|
188
195
|
}
|
|
189
196
|
/**
|
|
190
|
-
* Returns the
|
|
197
|
+
* Returns the configured Open Liquidity Strategy for the given chain.
|
|
191
198
|
*/
|
|
192
|
-
function
|
|
193
|
-
const
|
|
194
|
-
if (!
|
|
195
|
-
return
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
return [getAddress(address)];
|
|
204
|
-
}
|
|
205
|
-
catch {
|
|
206
|
-
return [];
|
|
207
|
-
}
|
|
208
|
-
});
|
|
199
|
+
function getOpenLiquidityStrategy(chainId) {
|
|
200
|
+
const strategyAddress = tryGetContractAddress(chainId, 'OpenLiquidityStrategy');
|
|
201
|
+
if (!strategyAddress)
|
|
202
|
+
return null;
|
|
203
|
+
try {
|
|
204
|
+
return getAddress(strategyAddress);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
209
|
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { getAddress } from 'viem';
|
|
2
|
+
import { LIQUIDITY_STRATEGY_ABI } from '../../core/abis';
|
|
3
|
+
import { multicall } from '../../utils/multicall';
|
|
4
|
+
// Liquidity strategy incentive rates are stored as 18-decimal percentages.
|
|
5
|
+
const FEE_DENOMINATOR = 10n ** 18n;
|
|
6
|
+
function toBigIntValue(value) {
|
|
7
|
+
return typeof value === 'bigint' ? value : BigInt(value);
|
|
8
|
+
}
|
|
9
|
+
function toNumberValue(value) {
|
|
10
|
+
return typeof value === 'number' ? value : Number(value);
|
|
11
|
+
}
|
|
12
|
+
function parseDirection(value) {
|
|
13
|
+
const normalized = toNumberValue(value);
|
|
14
|
+
if (normalized === 0)
|
|
15
|
+
return 'Expand';
|
|
16
|
+
if (normalized === 1)
|
|
17
|
+
return 'Contract';
|
|
18
|
+
throw new Error(`Unsupported liquidity strategy direction: ${normalized}`);
|
|
19
|
+
}
|
|
20
|
+
function parsePoolConfig(raw) {
|
|
21
|
+
return {
|
|
22
|
+
isToken0Debt: raw[0],
|
|
23
|
+
lastRebalance: toNumberValue(raw[1]),
|
|
24
|
+
rebalanceCooldown: toNumberValue(raw[2]),
|
|
25
|
+
protocolFeeRecipient: raw[3],
|
|
26
|
+
liquiditySourceIncentiveExpansion: toBigIntValue(raw[4]),
|
|
27
|
+
protocolIncentiveExpansion: toBigIntValue(raw[5]),
|
|
28
|
+
liquiditySourceIncentiveContraction: toBigIntValue(raw[6]),
|
|
29
|
+
protocolIncentiveContraction: toBigIntValue(raw[7]),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function parseContext(raw) {
|
|
33
|
+
return {
|
|
34
|
+
pool: raw[0],
|
|
35
|
+
reserves: {
|
|
36
|
+
reserveNum: toBigIntValue(raw[1][0]),
|
|
37
|
+
reserveDen: toBigIntValue(raw[1][1]),
|
|
38
|
+
},
|
|
39
|
+
prices: {
|
|
40
|
+
oracleNum: toBigIntValue(raw[2][0]),
|
|
41
|
+
oracleDen: toBigIntValue(raw[2][1]),
|
|
42
|
+
poolPriceAbove: raw[2][2],
|
|
43
|
+
rebalanceThreshold: toNumberValue(raw[2][3]),
|
|
44
|
+
},
|
|
45
|
+
token0: raw[3],
|
|
46
|
+
token1: raw[4],
|
|
47
|
+
token0Dec: toBigIntValue(raw[5]),
|
|
48
|
+
token1Dec: toBigIntValue(raw[6]),
|
|
49
|
+
isToken0Debt: raw[7],
|
|
50
|
+
incentives: {
|
|
51
|
+
liquiditySourceIncentiveExpansion: toBigIntValue(raw[8][0]),
|
|
52
|
+
protocolIncentiveExpansion: toBigIntValue(raw[8][1]),
|
|
53
|
+
liquiditySourceIncentiveContraction: toBigIntValue(raw[8][2]),
|
|
54
|
+
protocolIncentiveContraction: toBigIntValue(raw[8][3]),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function parseAction(raw) {
|
|
59
|
+
return {
|
|
60
|
+
dir: parseDirection(raw[0]),
|
|
61
|
+
amount0Out: toBigIntValue(raw[1]),
|
|
62
|
+
amount1Out: toBigIntValue(raw[2]),
|
|
63
|
+
amountOwedToPool: toBigIntValue(raw[3]),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function isPreviewEligible(detail) {
|
|
67
|
+
return (detail.poolType === 'FPMM' &&
|
|
68
|
+
detail.pricing !== null &&
|
|
69
|
+
detail.rebalancing.inBand === false &&
|
|
70
|
+
!!detail.rebalancing.liquidityStrategy);
|
|
71
|
+
}
|
|
72
|
+
function buildPreview(detail, strategyAddress, config, context, action) {
|
|
73
|
+
const debtToken = context.isToken0Debt ? context.token0 : context.token1;
|
|
74
|
+
const collateralToken = context.isToken0Debt ? context.token1 : context.token0;
|
|
75
|
+
const inputToken = action.dir === 'Expand' ? debtToken : collateralToken;
|
|
76
|
+
const outputToken = action.dir === 'Expand' ? collateralToken : debtToken;
|
|
77
|
+
const amountTransferredValue = action.amount0Out > 0n ? action.amount0Out : action.amount1Out;
|
|
78
|
+
const protocolRate = action.dir === 'Expand'
|
|
79
|
+
? config.protocolIncentiveExpansion
|
|
80
|
+
: config.protocolIncentiveContraction;
|
|
81
|
+
const liquiditySourceRate = action.dir === 'Expand'
|
|
82
|
+
? config.liquiditySourceIncentiveExpansion
|
|
83
|
+
: config.liquiditySourceIncentiveContraction;
|
|
84
|
+
const protocolIncentiveAmount = (amountTransferredValue * protocolRate) / FEE_DENOMINATOR;
|
|
85
|
+
const liquiditySourceBase = amountTransferredValue > protocolIncentiveAmount
|
|
86
|
+
? amountTransferredValue - protocolIncentiveAmount
|
|
87
|
+
: 0n;
|
|
88
|
+
const liquiditySourceIncentiveAmount = (liquiditySourceBase * liquiditySourceRate) / FEE_DENOMINATOR;
|
|
89
|
+
return {
|
|
90
|
+
poolAddress: detail.poolAddr,
|
|
91
|
+
strategyAddress,
|
|
92
|
+
direction: action.dir,
|
|
93
|
+
config,
|
|
94
|
+
context,
|
|
95
|
+
action,
|
|
96
|
+
inputToken,
|
|
97
|
+
outputToken,
|
|
98
|
+
amountRequired: {
|
|
99
|
+
token: inputToken,
|
|
100
|
+
amount: action.amountOwedToPool,
|
|
101
|
+
},
|
|
102
|
+
amountTransferred: {
|
|
103
|
+
token: outputToken,
|
|
104
|
+
amount: amountTransferredValue,
|
|
105
|
+
},
|
|
106
|
+
protocolIncentive: {
|
|
107
|
+
token: outputToken,
|
|
108
|
+
amount: protocolIncentiveAmount,
|
|
109
|
+
},
|
|
110
|
+
liquiditySourceIncentive: {
|
|
111
|
+
token: outputToken,
|
|
112
|
+
amount: liquiditySourceIncentiveAmount,
|
|
113
|
+
},
|
|
114
|
+
approvalToken: inputToken,
|
|
115
|
+
approvalSpender: strategyAddress,
|
|
116
|
+
approvalAmount: action.amountOwedToPool,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export async function fetchPoolRebalancePreview(publicClient, detail) {
|
|
120
|
+
const [preview] = await fetchPoolRebalancePreviewBatch(publicClient, [detail]);
|
|
121
|
+
return preview;
|
|
122
|
+
}
|
|
123
|
+
export async function fetchPoolRebalancePreviewBatch(publicClient, details) {
|
|
124
|
+
const previews = details.map(() => null);
|
|
125
|
+
const eligibleTargets = details.flatMap((detail, index) => {
|
|
126
|
+
if (!isPreviewEligible(detail))
|
|
127
|
+
return [];
|
|
128
|
+
const strategyAddress = detail.rebalancing.liquidityStrategy;
|
|
129
|
+
if (!strategyAddress)
|
|
130
|
+
return [];
|
|
131
|
+
try {
|
|
132
|
+
return [
|
|
133
|
+
{
|
|
134
|
+
index,
|
|
135
|
+
detail,
|
|
136
|
+
strategyAddress: getAddress(strategyAddress),
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
if (eligibleTargets.length === 0) {
|
|
145
|
+
return previews;
|
|
146
|
+
}
|
|
147
|
+
const contracts = eligibleTargets.flatMap(({ detail, strategyAddress }) => [
|
|
148
|
+
{
|
|
149
|
+
address: strategyAddress,
|
|
150
|
+
abi: LIQUIDITY_STRATEGY_ABI,
|
|
151
|
+
functionName: 'poolConfigs',
|
|
152
|
+
args: [detail.poolAddr],
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
address: strategyAddress,
|
|
156
|
+
abi: LIQUIDITY_STRATEGY_ABI,
|
|
157
|
+
functionName: 'determineAction',
|
|
158
|
+
args: [detail.poolAddr],
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
const results = await multicall(publicClient, contracts);
|
|
162
|
+
eligibleTargets.forEach((target, targetIndex) => {
|
|
163
|
+
const configResult = results[targetIndex * 2];
|
|
164
|
+
const determineActionResult = results[targetIndex * 2 + 1];
|
|
165
|
+
if (!configResult || !determineActionResult)
|
|
166
|
+
return;
|
|
167
|
+
if (configResult.status === 'failure' || determineActionResult.status === 'failure')
|
|
168
|
+
return;
|
|
169
|
+
try {
|
|
170
|
+
const config = parsePoolConfig(configResult.result);
|
|
171
|
+
const [rawContext, rawAction] = determineActionResult.result;
|
|
172
|
+
const context = parseContext(rawContext);
|
|
173
|
+
const action = parseAction(rawAction);
|
|
174
|
+
previews[target.index] = buildPreview(target.detail, target.strategyAddress, config, context, action);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
previews[target.index] = null;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return previews;
|
|
181
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PublicClient } from 'viem';
|
|
2
2
|
import { PoolService } from '../pools';
|
|
3
3
|
import { RouteService } from '../routes';
|
|
4
|
-
import { AddLiquidityInput, RemoveLiquidityInput, ZapInInput, ZapOutInput, PrepareZapInInput, PrepareZapOutInput, AddLiquidityQuote, RemoveLiquidityQuote, AddLiquidityDetails, RemoveLiquidityDetails, AddLiquidityTransaction, RemoveLiquidityTransaction, LPTokenBalance, LiquidityOptions, PreparedZapIn, PreparedZapOut, ZapInQuote, ZapOutQuote, ZapInDetails, ZapOutDetails, ZapInTransaction, ZapOutTransaction } from '../../core/types';
|
|
4
|
+
import { AddLiquidityInput, RemoveLiquidityInput, RebalanceDetails, RebalanceInput, RebalanceTransaction, ZapInInput, ZapOutInput, PrepareZapInInput, PrepareZapOutInput, AddLiquidityQuote, RemoveLiquidityQuote, AddLiquidityDetails, RemoveLiquidityDetails, AddLiquidityTransaction, RemoveLiquidityTransaction, LPTokenBalance, LiquidityOptions, PreparedZapIn, PreparedZapOut, ZapInQuote, ZapOutQuote, ZapInDetails, ZapOutDetails, ZapInTransaction, ZapOutTransaction } from '../../core/types';
|
|
5
5
|
export declare class LiquidityService {
|
|
6
6
|
private publicClient;
|
|
7
7
|
private chainId;
|
|
@@ -120,5 +120,20 @@ export declare class LiquidityService {
|
|
|
120
120
|
* @returns Expected output amount and minimum amounts after slippage
|
|
121
121
|
*/
|
|
122
122
|
quoteZapOut(poolAddress: string, tokenOut: string, liquidity: bigint, options: LiquidityOptions): Promise<ZapOutQuote>;
|
|
123
|
+
/**
|
|
124
|
+
* Builds rebalance transaction parameters without checking approval.
|
|
125
|
+
* Use buildRebalanceTransaction if you need approval handling.
|
|
126
|
+
* @param input - Rebalance parameters
|
|
127
|
+
* @returns Transaction details with encoded call data
|
|
128
|
+
*/
|
|
129
|
+
buildRebalanceParams(input: RebalanceInput): Promise<RebalanceDetails>;
|
|
130
|
+
/**
|
|
131
|
+
* Builds a rebalance transaction with ERC20 approval if needed.
|
|
132
|
+
* @param input - Rebalance parameters including owner for allowance checks
|
|
133
|
+
* @returns Transaction with approval (if needed) and rebalance call
|
|
134
|
+
*/
|
|
135
|
+
buildRebalanceTransaction(input: RebalanceInput & {
|
|
136
|
+
owner: string;
|
|
137
|
+
}): Promise<RebalanceTransaction>;
|
|
123
138
|
}
|
|
124
139
|
//# sourceMappingURL=LiquidityService.d.ts.map
|
|
@@ -4,6 +4,7 @@ exports.LiquidityService = void 0;
|
|
|
4
4
|
const basicLiquidity_1 = require("./basicLiquidity");
|
|
5
5
|
const zapIn_1 = require("./zapIn");
|
|
6
6
|
const zapOut_1 = require("./zapOut");
|
|
7
|
+
const rebalance_1 = require("./rebalance");
|
|
7
8
|
class LiquidityService {
|
|
8
9
|
constructor(publicClient, chainId, poolService, routeService) {
|
|
9
10
|
this.publicClient = publicClient;
|
|
@@ -145,6 +146,23 @@ class LiquidityService {
|
|
|
145
146
|
async quoteZapOut(poolAddress, tokenOut, liquidity, options) {
|
|
146
147
|
return (0, zapOut_1.quoteZapOutInternal)(this.publicClient, this.chainId, this.poolService, this.routeService, poolAddress, tokenOut, liquidity, options);
|
|
147
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Builds rebalance transaction parameters without checking approval.
|
|
151
|
+
* Use buildRebalanceTransaction if you need approval handling.
|
|
152
|
+
* @param input - Rebalance parameters
|
|
153
|
+
* @returns Transaction details with encoded call data
|
|
154
|
+
*/
|
|
155
|
+
async buildRebalanceParams(input) {
|
|
156
|
+
return (0, rebalance_1.buildRebalanceParamsInternal)(this.publicClient, this.chainId, this.poolService, input.poolAddress);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Builds a rebalance transaction with ERC20 approval if needed.
|
|
160
|
+
* @param input - Rebalance parameters including owner for allowance checks
|
|
161
|
+
* @returns Transaction with approval (if needed) and rebalance call
|
|
162
|
+
*/
|
|
163
|
+
async buildRebalanceTransaction(input) {
|
|
164
|
+
return (0, rebalance_1.buildRebalanceTransactionInternal)(this.publicClient, this.chainId, this.poolService, input.poolAddress, input.owner);
|
|
165
|
+
}
|
|
148
166
|
}
|
|
149
167
|
exports.LiquidityService = LiquidityService;
|
|
150
168
|
//# sourceMappingURL=LiquidityService.js.map
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Address, PublicClient } from 'viem';
|
|
2
2
|
import { PoolService } from '../pools';
|
|
3
3
|
import { CallParams } from '../../core/types';
|
|
4
|
-
export declare function buildApprovalParams(chainId: number, token: Address, amount: bigint): CallParams;
|
|
5
|
-
export declare function getAllowance(publicClient: PublicClient, token: Address, owner: Address, chainId: number): Promise<bigint>;
|
|
4
|
+
export declare function buildApprovalParams(chainId: number, token: Address, amount: bigint, spender?: Address): CallParams;
|
|
5
|
+
export declare function getAllowance(publicClient: PublicClient, token: Address, owner: Address, chainId: number, spender?: Address): Promise<bigint>;
|
|
6
6
|
export declare function calculateMinAmount(amount: bigint, slippageTolerance: number): bigint;
|
|
7
7
|
export declare function getPoolInfo(poolService: PoolService, poolAddress: string): Promise<{
|
|
8
8
|
token0: Address;
|
|
@@ -12,22 +12,25 @@ const abis_1 = require("../../core/abis");
|
|
|
12
12
|
const constants_1 = require("../../core/constants");
|
|
13
13
|
const validation_1 = require("../../utils/validation");
|
|
14
14
|
const multicall_1 = require("../../utils/multicall");
|
|
15
|
-
function
|
|
16
|
-
|
|
15
|
+
function getApprovalSpender(chainId, spender) {
|
|
16
|
+
return spender ?? (0, constants_1.getContractAddress)(chainId, 'Router');
|
|
17
|
+
}
|
|
18
|
+
function buildApprovalParams(chainId, token, amount, spender) {
|
|
19
|
+
const approvalSpender = getApprovalSpender(chainId, spender);
|
|
17
20
|
const data = (0, viem_1.encodeFunctionData)({
|
|
18
21
|
abi: abis_1.ERC20_ABI,
|
|
19
22
|
functionName: 'approve',
|
|
20
|
-
args: [
|
|
23
|
+
args: [approvalSpender, amount],
|
|
21
24
|
});
|
|
22
25
|
return { to: token, data, value: '0' };
|
|
23
26
|
}
|
|
24
|
-
async function getAllowance(publicClient, token, owner, chainId) {
|
|
25
|
-
const
|
|
27
|
+
async function getAllowance(publicClient, token, owner, chainId, spender) {
|
|
28
|
+
const approvalSpender = getApprovalSpender(chainId, spender);
|
|
26
29
|
return (await publicClient.readContract({
|
|
27
30
|
address: token,
|
|
28
31
|
abi: abis_1.ERC20_ABI,
|
|
29
32
|
functionName: 'allowance',
|
|
30
|
-
args: [owner,
|
|
33
|
+
args: [owner, approvalSpender],
|
|
31
34
|
}));
|
|
32
35
|
}
|
|
33
36
|
function calculateMinAmount(amount, slippageTolerance) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { PublicClient } from 'viem';
|
|
2
|
+
import { RebalanceDetails, RebalanceTransaction } from '../../core/types';
|
|
3
|
+
import { PoolService } from '../pools';
|
|
4
|
+
export declare function buildRebalanceParamsInternal(publicClient: PublicClient, chainId: number, poolService: PoolService, poolAddress: string): Promise<RebalanceDetails>;
|
|
5
|
+
export declare function buildRebalanceTransactionInternal(publicClient: PublicClient, chainId: number, poolService: PoolService, poolAddress: string, owner: string): Promise<RebalanceTransaction>;
|
|
6
|
+
//# sourceMappingURL=rebalance.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildRebalanceParamsInternal = buildRebalanceParamsInternal;
|
|
4
|
+
exports.buildRebalanceTransactionInternal = buildRebalanceTransactionInternal;
|
|
5
|
+
const viem_1 = require("viem");
|
|
6
|
+
const abis_1 = require("../../core/abis");
|
|
7
|
+
const validation_1 = require("../../utils/validation");
|
|
8
|
+
const liquidityHelpers_1 = require("./liquidityHelpers");
|
|
9
|
+
function getRebalanceUnavailableError(poolAddress) {
|
|
10
|
+
return new Error(`Pool ${poolAddress} is not currently rebalanceable or does not have a supported liquidity strategy.`);
|
|
11
|
+
}
|
|
12
|
+
function assertRebalanceActionAmounts(poolAddress, amountRequired, amountTransferred) {
|
|
13
|
+
if (amountRequired <= 0n || amountTransferred <= 0n) {
|
|
14
|
+
throw new Error(`Rebalance action for pool ${poolAddress} has zero amounts and cannot be executed.`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function buildRebalanceParamsInternal(publicClient, chainId, poolService, poolAddress) {
|
|
18
|
+
(0, validation_1.validateAddress)(poolAddress, 'poolAddress');
|
|
19
|
+
const preview = await poolService.getPoolRebalancePreview(poolAddress);
|
|
20
|
+
if (!preview) {
|
|
21
|
+
throw getRebalanceUnavailableError(poolAddress);
|
|
22
|
+
}
|
|
23
|
+
assertRebalanceActionAmounts(poolAddress, preview.amountRequired.amount, preview.amountTransferred.amount);
|
|
24
|
+
const data = (0, viem_1.encodeFunctionData)({
|
|
25
|
+
abi: abis_1.LIQUIDITY_STRATEGY_ABI,
|
|
26
|
+
functionName: 'rebalance',
|
|
27
|
+
args: [poolAddress],
|
|
28
|
+
});
|
|
29
|
+
return {
|
|
30
|
+
params: {
|
|
31
|
+
to: preview.strategyAddress,
|
|
32
|
+
data,
|
|
33
|
+
value: '0',
|
|
34
|
+
},
|
|
35
|
+
poolAddress,
|
|
36
|
+
strategyAddress: preview.strategyAddress,
|
|
37
|
+
inputToken: preview.inputToken,
|
|
38
|
+
outputToken: preview.outputToken,
|
|
39
|
+
amountRequired: preview.amountRequired.amount,
|
|
40
|
+
expectedAmountTransferred: preview.amountTransferred.amount,
|
|
41
|
+
expectedProtocolIncentive: preview.protocolIncentive.amount,
|
|
42
|
+
expectedLiquiditySourceIncentive: preview.liquiditySourceIncentive.amount,
|
|
43
|
+
approvalToken: preview.approvalToken,
|
|
44
|
+
approvalSpender: preview.approvalSpender,
|
|
45
|
+
approvalAmount: preview.approvalAmount,
|
|
46
|
+
direction: preview.direction,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
async function buildRebalanceTransactionInternal(publicClient, chainId, poolService, poolAddress, owner) {
|
|
50
|
+
(0, validation_1.validateAddress)(owner, 'owner');
|
|
51
|
+
const rebalance = await buildRebalanceParamsInternal(publicClient, chainId, poolService, poolAddress);
|
|
52
|
+
const approvalToken = rebalance.approvalToken;
|
|
53
|
+
const approvalSpender = rebalance.approvalSpender;
|
|
54
|
+
const currentAllowance = await (0, liquidityHelpers_1.getAllowance)(publicClient, approvalToken, owner, chainId, approvalSpender);
|
|
55
|
+
const approval = currentAllowance < rebalance.approvalAmount
|
|
56
|
+
? {
|
|
57
|
+
token: rebalance.approvalToken,
|
|
58
|
+
amount: rebalance.approvalAmount,
|
|
59
|
+
params: (0, liquidityHelpers_1.buildApprovalParams)(chainId, approvalToken, rebalance.approvalAmount, approvalSpender),
|
|
60
|
+
}
|
|
61
|
+
: null;
|
|
62
|
+
return { approval, rebalance };
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=rebalance.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Pool, PoolDetails } from '../../core/types';
|
|
1
|
+
import { Pool, PoolDetails, PoolRebalancePreview } from '../../core/types';
|
|
2
2
|
import { PublicClient } from 'viem';
|
|
3
3
|
/**
|
|
4
4
|
* Result of pool discovery including any warnings from failed factories
|
|
@@ -62,6 +62,8 @@ export declare class PoolService {
|
|
|
62
62
|
* ```
|
|
63
63
|
*/
|
|
64
64
|
getPoolDetails(poolAddr: string): Promise<PoolDetails>;
|
|
65
|
+
getPoolRebalancePreview(poolAddr: string): Promise<PoolRebalancePreview | null>;
|
|
66
|
+
getPoolRebalancePreviewBatch(poolAddresses?: string[]): Promise<Array<PoolRebalancePreview | null>>;
|
|
65
67
|
getPoolDetailsBatch(poolAddresses?: string[]): Promise<PoolDetails[]>;
|
|
66
68
|
}
|
|
67
69
|
//# sourceMappingURL=PoolService.d.ts.map
|
|
@@ -4,6 +4,7 @@ exports.PoolService = void 0;
|
|
|
4
4
|
const types_1 = require("../../core/types");
|
|
5
5
|
const poolDiscovery_1 = require("./poolDiscovery");
|
|
6
6
|
const poolDetails_1 = require("./poolDetails");
|
|
7
|
+
const rebalancePreview_1 = require("./rebalancePreview");
|
|
7
8
|
/**
|
|
8
9
|
* Service for discovering liquidity pools in the Mento protocol.
|
|
9
10
|
* Aggregates pools from multiple factory contracts (FPMM and VirtualPool).
|
|
@@ -107,6 +108,14 @@ class PoolService {
|
|
|
107
108
|
const [details] = await this.getPoolDetailsBatch([poolAddr]);
|
|
108
109
|
return details;
|
|
109
110
|
}
|
|
111
|
+
async getPoolRebalancePreview(poolAddr) {
|
|
112
|
+
const details = await this.getPoolDetails(poolAddr);
|
|
113
|
+
return (0, rebalancePreview_1.fetchPoolRebalancePreview)(this.publicClient, details);
|
|
114
|
+
}
|
|
115
|
+
async getPoolRebalancePreviewBatch(poolAddresses) {
|
|
116
|
+
const details = await this.getPoolDetailsBatch(poolAddresses);
|
|
117
|
+
return (0, rebalancePreview_1.fetchPoolRebalancePreviewBatch)(this.publicClient, details);
|
|
118
|
+
}
|
|
110
119
|
async getPoolDetailsBatch(poolAddresses) {
|
|
111
120
|
const pools = await this.getPools();
|
|
112
121
|
const targets = poolAddresses
|
|
@@ -21,17 +21,18 @@ async function fetchFPMMPoolDetailsBatch(publicClient, chainId, pools) {
|
|
|
21
21
|
if (pools.length === 0) {
|
|
22
22
|
return [];
|
|
23
23
|
}
|
|
24
|
-
const
|
|
25
|
-
const contracts = pools.flatMap((pool) => buildFPMMContracts(pool,
|
|
24
|
+
const openLiquidityStrategy = getOpenLiquidityStrategy(chainId);
|
|
25
|
+
const contracts = pools.flatMap((pool) => buildFPMMContracts(pool, openLiquidityStrategy));
|
|
26
26
|
const results = await (0, multicall_1.multicall)(publicClient, contracts);
|
|
27
|
-
const
|
|
27
|
+
const strategyCheckCount = openLiquidityStrategy ? 1 : 0;
|
|
28
|
+
const perPoolResultCount = FPMM_FIXED_RESULT_COUNT + strategyCheckCount + 1;
|
|
28
29
|
return pools.map((pool, index) => {
|
|
29
30
|
const offset = index * perPoolResultCount;
|
|
30
31
|
const poolResults = results.slice(offset, offset + perPoolResultCount);
|
|
31
|
-
return parseFPMMPoolDetails(pool,
|
|
32
|
+
return parseFPMMPoolDetails(pool, openLiquidityStrategy, poolResults);
|
|
32
33
|
});
|
|
33
34
|
}
|
|
34
|
-
function buildFPMMContracts(pool,
|
|
35
|
+
function buildFPMMContracts(pool, openLiquidityStrategy) {
|
|
35
36
|
const address = pool.poolAddr;
|
|
36
37
|
return [
|
|
37
38
|
{ address, abi: abis_1.FPMM_ABI, functionName: 'getReserves' },
|
|
@@ -42,16 +43,18 @@ function buildFPMMContracts(pool, knownStrategies) {
|
|
|
42
43
|
{ address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceIncentive' },
|
|
43
44
|
{ address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceThresholdAbove' },
|
|
44
45
|
{ address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceThresholdBelow' },
|
|
45
|
-
...
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
...(openLiquidityStrategy
|
|
47
|
+
? [{
|
|
48
|
+
address,
|
|
49
|
+
abi: abis_1.FPMM_ABI,
|
|
50
|
+
functionName: 'liquidityStrategy',
|
|
51
|
+
args: [openLiquidityStrategy],
|
|
52
|
+
}]
|
|
53
|
+
: []),
|
|
51
54
|
{ address, abi: abis_1.FPMM_ABI, functionName: 'getRebalancingState' },
|
|
52
55
|
];
|
|
53
56
|
}
|
|
54
|
-
function parseFPMMPoolDetails(pool,
|
|
57
|
+
function parseFPMMPoolDetails(pool, openLiquidityStrategy, results) {
|
|
55
58
|
try {
|
|
56
59
|
const reservesRes = results[0];
|
|
57
60
|
const decimals0Res = results[1];
|
|
@@ -85,10 +88,14 @@ function parseFPMMPoolDetails(pool, knownStrategies, results) {
|
|
|
85
88
|
const rebalanceIncentiveBps = rebalanceIncentiveRes.result;
|
|
86
89
|
const thresholdAboveBps = thresholdAboveRes.result;
|
|
87
90
|
const thresholdBelowBps = thresholdBelowRes.result;
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const liquidityStrategy =
|
|
91
|
-
|
|
91
|
+
const strategyCheckCount = openLiquidityStrategy ? 1 : 0;
|
|
92
|
+
const openStrategyResult = strategyCheckCount > 0 ? results[FPMM_FIXED_RESULT_COUNT] : null;
|
|
93
|
+
const liquidityStrategy = openLiquidityStrategy &&
|
|
94
|
+
openStrategyResult?.status === 'success' &&
|
|
95
|
+
openStrategyResult.result === true
|
|
96
|
+
? openLiquidityStrategy
|
|
97
|
+
: null;
|
|
98
|
+
const rebalancingRes = results[FPMM_FIXED_RESULT_COUNT + strategyCheckCount];
|
|
92
99
|
let pricing = null;
|
|
93
100
|
let inBand = null;
|
|
94
101
|
if (rebalancingRes?.status === 'success') {
|
|
@@ -193,24 +200,17 @@ function parseVirtualPoolDetails(pool, results) {
|
|
|
193
200
|
}
|
|
194
201
|
}
|
|
195
202
|
/**
|
|
196
|
-
* Returns the
|
|
203
|
+
* Returns the configured Open Liquidity Strategy for the given chain.
|
|
197
204
|
*/
|
|
198
|
-
function
|
|
199
|
-
const
|
|
200
|
-
if (!
|
|
201
|
-
return
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
return [(0, viem_1.getAddress)(address)];
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
return [];
|
|
213
|
-
}
|
|
214
|
-
});
|
|
205
|
+
function getOpenLiquidityStrategy(chainId) {
|
|
206
|
+
const strategyAddress = (0, constants_1.tryGetContractAddress)(chainId, 'OpenLiquidityStrategy');
|
|
207
|
+
if (!strategyAddress)
|
|
208
|
+
return null;
|
|
209
|
+
try {
|
|
210
|
+
return (0, viem_1.getAddress)(strategyAddress);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
215
|
}
|
|
216
216
|
//# sourceMappingURL=poolDetails.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type PublicClient } from 'viem';
|
|
2
|
+
import type { PoolDetails, PoolRebalancePreview } from '../../core/types';
|
|
3
|
+
export declare function fetchPoolRebalancePreview(publicClient: PublicClient, detail: PoolDetails): Promise<PoolRebalancePreview | null>;
|
|
4
|
+
export declare function fetchPoolRebalancePreviewBatch(publicClient: PublicClient, details: PoolDetails[]): Promise<Array<PoolRebalancePreview | null>>;
|
|
5
|
+
//# sourceMappingURL=rebalancePreview.d.ts.map
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchPoolRebalancePreview = fetchPoolRebalancePreview;
|
|
4
|
+
exports.fetchPoolRebalancePreviewBatch = fetchPoolRebalancePreviewBatch;
|
|
5
|
+
const viem_1 = require("viem");
|
|
6
|
+
const abis_1 = require("../../core/abis");
|
|
7
|
+
const multicall_1 = require("../../utils/multicall");
|
|
8
|
+
// Liquidity strategy incentive rates are stored as 18-decimal percentages.
|
|
9
|
+
const FEE_DENOMINATOR = 10n ** 18n;
|
|
10
|
+
function toBigIntValue(value) {
|
|
11
|
+
return typeof value === 'bigint' ? value : BigInt(value);
|
|
12
|
+
}
|
|
13
|
+
function toNumberValue(value) {
|
|
14
|
+
return typeof value === 'number' ? value : Number(value);
|
|
15
|
+
}
|
|
16
|
+
function parseDirection(value) {
|
|
17
|
+
const normalized = toNumberValue(value);
|
|
18
|
+
if (normalized === 0)
|
|
19
|
+
return 'Expand';
|
|
20
|
+
if (normalized === 1)
|
|
21
|
+
return 'Contract';
|
|
22
|
+
throw new Error(`Unsupported liquidity strategy direction: ${normalized}`);
|
|
23
|
+
}
|
|
24
|
+
function parsePoolConfig(raw) {
|
|
25
|
+
return {
|
|
26
|
+
isToken0Debt: raw[0],
|
|
27
|
+
lastRebalance: toNumberValue(raw[1]),
|
|
28
|
+
rebalanceCooldown: toNumberValue(raw[2]),
|
|
29
|
+
protocolFeeRecipient: raw[3],
|
|
30
|
+
liquiditySourceIncentiveExpansion: toBigIntValue(raw[4]),
|
|
31
|
+
protocolIncentiveExpansion: toBigIntValue(raw[5]),
|
|
32
|
+
liquiditySourceIncentiveContraction: toBigIntValue(raw[6]),
|
|
33
|
+
protocolIncentiveContraction: toBigIntValue(raw[7]),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function parseContext(raw) {
|
|
37
|
+
return {
|
|
38
|
+
pool: raw[0],
|
|
39
|
+
reserves: {
|
|
40
|
+
reserveNum: toBigIntValue(raw[1][0]),
|
|
41
|
+
reserveDen: toBigIntValue(raw[1][1]),
|
|
42
|
+
},
|
|
43
|
+
prices: {
|
|
44
|
+
oracleNum: toBigIntValue(raw[2][0]),
|
|
45
|
+
oracleDen: toBigIntValue(raw[2][1]),
|
|
46
|
+
poolPriceAbove: raw[2][2],
|
|
47
|
+
rebalanceThreshold: toNumberValue(raw[2][3]),
|
|
48
|
+
},
|
|
49
|
+
token0: raw[3],
|
|
50
|
+
token1: raw[4],
|
|
51
|
+
token0Dec: toBigIntValue(raw[5]),
|
|
52
|
+
token1Dec: toBigIntValue(raw[6]),
|
|
53
|
+
isToken0Debt: raw[7],
|
|
54
|
+
incentives: {
|
|
55
|
+
liquiditySourceIncentiveExpansion: toBigIntValue(raw[8][0]),
|
|
56
|
+
protocolIncentiveExpansion: toBigIntValue(raw[8][1]),
|
|
57
|
+
liquiditySourceIncentiveContraction: toBigIntValue(raw[8][2]),
|
|
58
|
+
protocolIncentiveContraction: toBigIntValue(raw[8][3]),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function parseAction(raw) {
|
|
63
|
+
return {
|
|
64
|
+
dir: parseDirection(raw[0]),
|
|
65
|
+
amount0Out: toBigIntValue(raw[1]),
|
|
66
|
+
amount1Out: toBigIntValue(raw[2]),
|
|
67
|
+
amountOwedToPool: toBigIntValue(raw[3]),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function isPreviewEligible(detail) {
|
|
71
|
+
return (detail.poolType === 'FPMM' &&
|
|
72
|
+
detail.pricing !== null &&
|
|
73
|
+
detail.rebalancing.inBand === false &&
|
|
74
|
+
!!detail.rebalancing.liquidityStrategy);
|
|
75
|
+
}
|
|
76
|
+
function buildPreview(detail, strategyAddress, config, context, action) {
|
|
77
|
+
const debtToken = context.isToken0Debt ? context.token0 : context.token1;
|
|
78
|
+
const collateralToken = context.isToken0Debt ? context.token1 : context.token0;
|
|
79
|
+
const inputToken = action.dir === 'Expand' ? debtToken : collateralToken;
|
|
80
|
+
const outputToken = action.dir === 'Expand' ? collateralToken : debtToken;
|
|
81
|
+
const amountTransferredValue = action.amount0Out > 0n ? action.amount0Out : action.amount1Out;
|
|
82
|
+
const protocolRate = action.dir === 'Expand'
|
|
83
|
+
? config.protocolIncentiveExpansion
|
|
84
|
+
: config.protocolIncentiveContraction;
|
|
85
|
+
const liquiditySourceRate = action.dir === 'Expand'
|
|
86
|
+
? config.liquiditySourceIncentiveExpansion
|
|
87
|
+
: config.liquiditySourceIncentiveContraction;
|
|
88
|
+
const protocolIncentiveAmount = (amountTransferredValue * protocolRate) / FEE_DENOMINATOR;
|
|
89
|
+
const liquiditySourceBase = amountTransferredValue > protocolIncentiveAmount
|
|
90
|
+
? amountTransferredValue - protocolIncentiveAmount
|
|
91
|
+
: 0n;
|
|
92
|
+
const liquiditySourceIncentiveAmount = (liquiditySourceBase * liquiditySourceRate) / FEE_DENOMINATOR;
|
|
93
|
+
return {
|
|
94
|
+
poolAddress: detail.poolAddr,
|
|
95
|
+
strategyAddress,
|
|
96
|
+
direction: action.dir,
|
|
97
|
+
config,
|
|
98
|
+
context,
|
|
99
|
+
action,
|
|
100
|
+
inputToken,
|
|
101
|
+
outputToken,
|
|
102
|
+
amountRequired: {
|
|
103
|
+
token: inputToken,
|
|
104
|
+
amount: action.amountOwedToPool,
|
|
105
|
+
},
|
|
106
|
+
amountTransferred: {
|
|
107
|
+
token: outputToken,
|
|
108
|
+
amount: amountTransferredValue,
|
|
109
|
+
},
|
|
110
|
+
protocolIncentive: {
|
|
111
|
+
token: outputToken,
|
|
112
|
+
amount: protocolIncentiveAmount,
|
|
113
|
+
},
|
|
114
|
+
liquiditySourceIncentive: {
|
|
115
|
+
token: outputToken,
|
|
116
|
+
amount: liquiditySourceIncentiveAmount,
|
|
117
|
+
},
|
|
118
|
+
approvalToken: inputToken,
|
|
119
|
+
approvalSpender: strategyAddress,
|
|
120
|
+
approvalAmount: action.amountOwedToPool,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async function fetchPoolRebalancePreview(publicClient, detail) {
|
|
124
|
+
const [preview] = await fetchPoolRebalancePreviewBatch(publicClient, [detail]);
|
|
125
|
+
return preview;
|
|
126
|
+
}
|
|
127
|
+
async function fetchPoolRebalancePreviewBatch(publicClient, details) {
|
|
128
|
+
const previews = details.map(() => null);
|
|
129
|
+
const eligibleTargets = details.flatMap((detail, index) => {
|
|
130
|
+
if (!isPreviewEligible(detail))
|
|
131
|
+
return [];
|
|
132
|
+
const strategyAddress = detail.rebalancing.liquidityStrategy;
|
|
133
|
+
if (!strategyAddress)
|
|
134
|
+
return [];
|
|
135
|
+
try {
|
|
136
|
+
return [
|
|
137
|
+
{
|
|
138
|
+
index,
|
|
139
|
+
detail,
|
|
140
|
+
strategyAddress: (0, viem_1.getAddress)(strategyAddress),
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
if (eligibleTargets.length === 0) {
|
|
149
|
+
return previews;
|
|
150
|
+
}
|
|
151
|
+
const contracts = eligibleTargets.flatMap(({ detail, strategyAddress }) => [
|
|
152
|
+
{
|
|
153
|
+
address: strategyAddress,
|
|
154
|
+
abi: abis_1.LIQUIDITY_STRATEGY_ABI,
|
|
155
|
+
functionName: 'poolConfigs',
|
|
156
|
+
args: [detail.poolAddr],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
address: strategyAddress,
|
|
160
|
+
abi: abis_1.LIQUIDITY_STRATEGY_ABI,
|
|
161
|
+
functionName: 'determineAction',
|
|
162
|
+
args: [detail.poolAddr],
|
|
163
|
+
},
|
|
164
|
+
]);
|
|
165
|
+
const results = await (0, multicall_1.multicall)(publicClient, contracts);
|
|
166
|
+
eligibleTargets.forEach((target, targetIndex) => {
|
|
167
|
+
const configResult = results[targetIndex * 2];
|
|
168
|
+
const determineActionResult = results[targetIndex * 2 + 1];
|
|
169
|
+
if (!configResult || !determineActionResult)
|
|
170
|
+
return;
|
|
171
|
+
if (configResult.status === 'failure' || determineActionResult.status === 'failure')
|
|
172
|
+
return;
|
|
173
|
+
try {
|
|
174
|
+
const config = parsePoolConfig(configResult.result);
|
|
175
|
+
const [rawContext, rawAction] = determineActionResult.result;
|
|
176
|
+
const context = parseContext(rawContext);
|
|
177
|
+
const action = parseAction(rawAction);
|
|
178
|
+
previews[target.index] = buildPreview(target.detail, target.strategyAddress, config, context, action);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
previews[target.index] = null;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
return previews;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=rebalancePreview.js.map
|