@haven-fi/solauto-sdk 1.0.171 → 1.0.173
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/transactions/transactionsManager.js +1 -1
- package/dist/utils/generalUtils.d.ts.map +1 -1
- package/dist/utils/generalUtils.js +24 -19
- package/dist/utils/solauto/rebalanceUtils.d.ts.map +1 -1
- package/dist/utils/solauto/rebalanceUtils.js +11 -18
- package/package.json +1 -1
- package/src/transactions/transactionsManager.ts +1 -1
- package/src/utils/generalUtils.ts +38 -20
- package/src/utils/solauto/rebalanceUtils.ts +21 -29
@@ -191,7 +191,7 @@ class TransactionsManager {
|
|
191
191
|
}
|
192
192
|
}
|
193
193
|
this.txHandler.log(`${name} is ${status.toString().toLowerCase()}`);
|
194
|
-
this.statusCallback?.(this.statuses);
|
194
|
+
this.statusCallback?.([...this.statuses]);
|
195
195
|
}
|
196
196
|
// TODO remove me
|
197
197
|
async debugAccounts(itemSet, tx) {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"generalUtils.d.ts","sourceRoot":"","sources":["../../src/utils/generalUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,
|
1
|
+
{"version":3,"file":"generalUtils.d.ts","sourceRoot":"","sources":["../../src/utils/generalUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EACL,eAAe,EAEf,GAAG,EACH,SAAS,IAAI,YAAY,EAC1B,MAAM,0BAA0B,CAAC;AAKlC,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAO1C;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,GAAG,EACR,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEnE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAU1E;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwD5E;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,GACzC,MAAM,GAAG,SAAS,CAKpB;AAED,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC;AAEjE,wBAAgB,2BAA2B,CAAC,CAAC,EAC3C,EAAE,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EACtC,OAAO,GAAE,MAAU,EACnB,KAAK,GAAE,MAAY,EACnB,aAAa,CAAC,EAAE,aAAa,GAC5B,OAAO,CAAC,CAAC,CAAC,CA8BZ"}
|
@@ -28,7 +28,9 @@ function currentUnixSeconds() {
|
|
28
28
|
return Math.round(new Date().getTime() / 1000);
|
29
29
|
}
|
30
30
|
async function getSolanaAccountCreated(umi, pk) {
|
31
|
-
const account = await umi.rpc.getAccount((0, umi_1.publicKey)(pk), {
|
31
|
+
const account = await umi.rpc.getAccount((0, umi_1.publicKey)(pk), {
|
32
|
+
commitment: "confirmed",
|
33
|
+
});
|
32
34
|
return rpcAccountCreated(account);
|
33
35
|
}
|
34
36
|
function rpcAccountCreated(account) {
|
@@ -53,25 +55,28 @@ async function fetchTokenPrices(mints) {
|
|
53
55
|
}
|
54
56
|
const priceFeedIds = mints.map((mint) => pythConstants_1.PYTH_PRICE_FEED_IDS[mint.toString()]);
|
55
57
|
const getReq = async () => await fetch(`https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds.map((x) => `ids%5B%5D=${x}`).join("&")}`);
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
const json = await resp.json();
|
64
|
-
const prices = json.parsed.map((x) => {
|
65
|
-
if (x.price.expo > 0) {
|
66
|
-
return Number((0, numberUtils_1.toBaseUnit)(Number(x.price.price), x.price.expo));
|
67
|
-
}
|
68
|
-
else if (x.price.expo < 0) {
|
69
|
-
return (0, numberUtils_1.fromBaseUnit)(BigInt(x.price.price), Math.abs(x.price.expo));
|
58
|
+
const prices = await retryWithExponentialBackoff(async () => {
|
59
|
+
let resp = await getReq();
|
60
|
+
let status = resp.status;
|
61
|
+
while (status !== 200) {
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
63
|
+
resp = await getReq();
|
64
|
+
status = resp.status;
|
70
65
|
}
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
66
|
+
const json = await resp.json();
|
67
|
+
const prices = json.parsed.map((x) => {
|
68
|
+
if (x.price.expo > 0) {
|
69
|
+
return Number((0, numberUtils_1.toBaseUnit)(Number(x.price.price), x.price.expo));
|
70
|
+
}
|
71
|
+
else if (x.price.expo < 0) {
|
72
|
+
return (0, numberUtils_1.fromBaseUnit)(BigInt(x.price.price), Math.abs(x.price.expo));
|
73
|
+
}
|
74
|
+
else {
|
75
|
+
return Number(x.price.price);
|
76
|
+
}
|
77
|
+
});
|
78
|
+
return prices;
|
79
|
+
}, 5, 200);
|
75
80
|
for (var i = 0; i < mints.length; i++) {
|
76
81
|
solautoConstants_1.PRICES[mints[i].toString()] = {
|
77
82
|
price: prices[i],
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"rebalanceUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/solauto/rebalanceUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EACL,WAAW,EACX,aAAa,EAEb,yBAAyB,EACzB,SAAS,EACV,MAAM,iBAAiB,CAAC;AAOzB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AA+IjD,MAAM,WAAW,eAAe;IAC9B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,yBAAyB,GAAG,SAAS,EAC/C,GAAG,EAAE,WAAW,GAAG,SAAS,EAC5B,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,2BAA2B,CAAC,EAAE,MAAM,EACpC,WAAW,CAAC,EAAE,MAAM,GACnB,eAAe,
|
1
|
+
{"version":3,"file":"rebalanceUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/solauto/rebalanceUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EACL,WAAW,EACX,aAAa,EAEb,yBAAyB,EACzB,SAAS,EACV,MAAM,iBAAiB,CAAC;AAOzB,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AA+IjD,MAAM,WAAW,eAAe;IAC9B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,SAAS,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,yBAAyB,GAAG,SAAS,EAC/C,GAAG,EAAE,WAAW,GAAG,SAAS,EAC5B,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,2BAA2B,CAAC,EAAE,MAAM,EACpC,WAAW,CAAC,EAAE,MAAM,GACnB,eAAe,CAoEjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,aAAa,EACvB,cAAc,EAAE,MAAM,GACrB,gBAAgB,GAAG,SAAS,CA0D9B;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,eAAe,EACvB,2BAA2B,CAAC,EAAE,MAAM,EACpC,UAAU,CAAC,EAAE,MAAM,GAClB,cAAc,CAyChB"}
|
@@ -120,16 +120,11 @@ function getFlashLoanDetails(client, values, jupQuote, priceImpactBps) {
|
|
120
120
|
let supplyUsd = (0, numberUtils_1.fromBaseUnit)(client.solautoPositionState.supply.amountUsed.baseAmountUsdValue, generalAccounts_1.USD_DECIMALS) +
|
121
121
|
(values.dcaTokenType === generated_1.TokenType.Supply ? values.amountUsdToDcaIn : 0);
|
122
122
|
let debtUsd = (0, numberUtils_1.fromBaseUnit)(client.solautoPositionState.debt.amountUsed.baseAmountUsdValue, generalAccounts_1.USD_DECIMALS);
|
123
|
-
const
|
124
|
-
Math.abs(values.debtAdjustmentUsd) * (0, numberUtils_1.fromBps)(priceImpactBps);
|
123
|
+
const debtAdjustmentUsd = Math.abs(values.debtAdjustmentUsd);
|
125
124
|
supplyUsd =
|
126
|
-
values.debtAdjustmentUsd < 0
|
127
|
-
? supplyUsd - debtAdjustmentWithSlippage
|
128
|
-
: supplyUsd;
|
125
|
+
values.debtAdjustmentUsd < 0 ? supplyUsd - debtAdjustmentUsd : supplyUsd;
|
129
126
|
debtUsd =
|
130
|
-
values.debtAdjustmentUsd > 0
|
131
|
-
? debtUsd + debtAdjustmentWithSlippage
|
132
|
-
: debtUsd;
|
127
|
+
values.debtAdjustmentUsd > 0 ? debtUsd + debtAdjustmentUsd : debtUsd;
|
133
128
|
const tempLiqUtilizationRateBps = (0, numberUtils_1.getLiqUtilzationRateBps)(supplyUsd, debtUsd, client.solautoPositionState.liqThresholdBps);
|
134
129
|
const requiresFlashLoan = supplyUsd <= 0 ||
|
135
130
|
tempLiqUtilizationRateBps >
|
@@ -151,7 +146,7 @@ function getFlashLoanDetails(client, values, jupQuote, priceImpactBps) {
|
|
151
146
|
? {
|
152
147
|
baseUnitAmount: exactAmountBaseUnit
|
153
148
|
? exactAmountBaseUnit
|
154
|
-
: (0, numberUtils_1.toBaseUnit)(
|
149
|
+
: (0, numberUtils_1.toBaseUnit)(debtAdjustmentUsd / flashLoanTokenPrice, flashLoanToken.decimals),
|
155
150
|
mint: (0, umi_web3js_adapters_1.toWeb3JsPublicKey)(flashLoanToken.mint),
|
156
151
|
}
|
157
152
|
: undefined;
|
@@ -166,7 +161,12 @@ function getJupSwapRebalanceDetails(client, values, targetLiqUtilizationRateBps,
|
|
166
161
|
const usdToSwap = Math.abs(values.debtAdjustmentUsd) +
|
167
162
|
(values.dcaTokenType === generated_1.TokenType.Debt ? values.amountUsdToDcaIn : 0);
|
168
163
|
const inputAmount = (0, numberUtils_1.toBaseUnit)(usdToSwap / (0, generalUtils_2.safeGetPrice)(input.mint), input.decimals);
|
169
|
-
const outputAmount =
|
164
|
+
const outputAmount = targetLiqUtilizationRateBps === 0
|
165
|
+
? output.amountUsed.baseUnit +
|
166
|
+
BigInt(Math.round(Number(output.amountUsed.baseUnit) *
|
167
|
+
// Add this small percentage to account for the APR on the debt between now and the transaction
|
168
|
+
0.0001))
|
169
|
+
: (0, numberUtils_1.toBaseUnit)(usdToSwap / (0, generalUtils_2.safeGetPrice)(output.mint), output.decimals);
|
170
170
|
const exactOut = targetLiqUtilizationRateBps === 0 || values.repayingCloseToMaxLtv;
|
171
171
|
const exactIn = !exactOut;
|
172
172
|
return {
|
@@ -174,14 +174,7 @@ function getJupSwapRebalanceDetails(client, values, targetLiqUtilizationRateBps,
|
|
174
174
|
outputMint: (0, umi_web3js_adapters_1.toWeb3JsPublicKey)(output.mint),
|
175
175
|
destinationWallet: client.solautoPosition,
|
176
176
|
slippageIncFactor: 0.5 + (attemptNum ?? 0) * 0.2,
|
177
|
-
amount: exactOut
|
178
|
-
? outputAmount +
|
179
|
-
(targetLiqUtilizationRateBps === 0
|
180
|
-
? BigInt(Math.round(Number(outputAmount) *
|
181
|
-
// Add this small percentage to account for the APR on the debt between now and the transaction
|
182
|
-
0.0001))
|
183
|
-
: BigInt(0))
|
184
|
-
: inputAmount,
|
177
|
+
amount: exactOut ? outputAmount : inputAmount,
|
185
178
|
exactIn: exactIn,
|
186
179
|
exactOut: exactOut,
|
187
180
|
};
|
package/package.json
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
import { PublicKey } from "@solana/web3.js";
|
2
|
-
import {
|
2
|
+
import {
|
3
|
+
MaybeRpcAccount,
|
4
|
+
publicKey,
|
5
|
+
Umi,
|
6
|
+
PublicKey as UmiPublicKey,
|
7
|
+
} from "@metaplex-foundation/umi";
|
3
8
|
import { PYTH_PRICE_FEED_IDS } from "../constants/pythConstants";
|
4
9
|
import { fromBaseUnit, toBaseUnit } from "./numberUtils";
|
5
10
|
import { PRICES } from "../constants/solautoConstants";
|
@@ -25,7 +30,9 @@ export async function getSolanaAccountCreated(
|
|
25
30
|
umi: Umi,
|
26
31
|
pk: PublicKey
|
27
32
|
): Promise<boolean> {
|
28
|
-
const account = await umi.rpc.getAccount(publicKey(pk), {
|
33
|
+
const account = await umi.rpc.getAccount(publicKey(pk), {
|
34
|
+
commitment: "confirmed",
|
35
|
+
});
|
29
36
|
return rpcAccountCreated(account);
|
30
37
|
}
|
31
38
|
|
@@ -65,24 +72,33 @@ export async function fetchTokenPrices(mints: PublicKey[]): Promise<number[]> {
|
|
65
72
|
await fetch(
|
66
73
|
`https://hermes.pyth.network/v2/updates/price/latest?${priceFeedIds.map((x) => `ids%5B%5D=${x}`).join("&")}`
|
67
74
|
);
|
68
|
-
let resp = await getReq();
|
69
|
-
let status = resp.status;
|
70
|
-
while (status !== 200) {
|
71
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
72
|
-
resp = await getReq();
|
73
|
-
status = resp.status;
|
74
|
-
}
|
75
75
|
|
76
|
-
const
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
76
|
+
const prices = await retryWithExponentialBackoff(
|
77
|
+
async () => {
|
78
|
+
let resp = await getReq();
|
79
|
+
let status = resp.status;
|
80
|
+
while (status !== 200) {
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
82
|
+
resp = await getReq();
|
83
|
+
status = resp.status;
|
84
|
+
}
|
85
|
+
|
86
|
+
const json = await resp.json();
|
87
|
+
const prices = json.parsed.map((x: any) => {
|
88
|
+
if (x.price.expo > 0) {
|
89
|
+
return Number(toBaseUnit(Number(x.price.price), x.price.expo));
|
90
|
+
} else if (x.price.expo < 0) {
|
91
|
+
return fromBaseUnit(BigInt(x.price.price), Math.abs(x.price.expo));
|
92
|
+
} else {
|
93
|
+
return Number(x.price.price);
|
94
|
+
}
|
95
|
+
});
|
96
|
+
|
97
|
+
return prices;
|
98
|
+
},
|
99
|
+
5,
|
100
|
+
200
|
101
|
+
);
|
86
102
|
|
87
103
|
for (var i = 0; i < mints.length; i++) {
|
88
104
|
PRICES[mints[i].toString()] = {
|
@@ -94,7 +110,9 @@ export async function fetchTokenPrices(mints: PublicKey[]): Promise<number[]> {
|
|
94
110
|
return prices;
|
95
111
|
}
|
96
112
|
|
97
|
-
export function safeGetPrice(
|
113
|
+
export function safeGetPrice(
|
114
|
+
mint: PublicKey | UmiPublicKey | undefined
|
115
|
+
): number | undefined {
|
98
116
|
if (mint && mint?.toString() in PRICES) {
|
99
117
|
return PRICES[mint!.toString()].price;
|
100
118
|
}
|
@@ -237,7 +237,8 @@ export function getRebalanceValues(
|
|
237
237
|
return {
|
238
238
|
increasingLeverage,
|
239
239
|
debtAdjustmentUsd,
|
240
|
-
repayingCloseToMaxLtv:
|
240
|
+
repayingCloseToMaxLtv:
|
241
|
+
state.liqUtilizationRateBps > maxRepayTo && targetRateBps >= maxRepayTo,
|
241
242
|
amountToDcaIn: amountToDcaIn ?? 0,
|
242
243
|
amountUsdToDcaIn,
|
243
244
|
dcaTokenType: dca?.tokenType,
|
@@ -266,17 +267,11 @@ export function getFlashLoanDetails(
|
|
266
267
|
USD_DECIMALS
|
267
268
|
);
|
268
269
|
|
269
|
-
const
|
270
|
-
Math.abs(values.debtAdjustmentUsd) +
|
271
|
-
Math.abs(values.debtAdjustmentUsd) * fromBps(priceImpactBps);
|
270
|
+
const debtAdjustmentUsd = Math.abs(values.debtAdjustmentUsd);
|
272
271
|
supplyUsd =
|
273
|
-
values.debtAdjustmentUsd < 0
|
274
|
-
? supplyUsd - debtAdjustmentWithSlippage
|
275
|
-
: supplyUsd;
|
272
|
+
values.debtAdjustmentUsd < 0 ? supplyUsd - debtAdjustmentUsd : supplyUsd;
|
276
273
|
debtUsd =
|
277
|
-
values.debtAdjustmentUsd > 0
|
278
|
-
? debtUsd + debtAdjustmentWithSlippage
|
279
|
-
: debtUsd;
|
274
|
+
values.debtAdjustmentUsd > 0 ? debtUsd + debtAdjustmentUsd : debtUsd;
|
280
275
|
|
281
276
|
const tempLiqUtilizationRateBps = getLiqUtilzationRateBps(
|
282
277
|
supplyUsd,
|
@@ -312,7 +307,7 @@ export function getFlashLoanDetails(
|
|
312
307
|
baseUnitAmount: exactAmountBaseUnit
|
313
308
|
? exactAmountBaseUnit
|
314
309
|
: toBaseUnit(
|
315
|
-
|
310
|
+
debtAdjustmentUsd / flashLoanTokenPrice,
|
316
311
|
flashLoanToken.decimals
|
317
312
|
),
|
318
313
|
mint: toWeb3JsPublicKey(flashLoanToken.mint),
|
@@ -341,12 +336,20 @@ export function getJupSwapRebalanceDetails(
|
|
341
336
|
usdToSwap / safeGetPrice(input.mint)!,
|
342
337
|
input.decimals
|
343
338
|
);
|
344
|
-
const outputAmount =
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
339
|
+
const outputAmount =
|
340
|
+
targetLiqUtilizationRateBps === 0
|
341
|
+
? output.amountUsed.baseUnit +
|
342
|
+
BigInt(
|
343
|
+
Math.round(
|
344
|
+
Number(output.amountUsed.baseUnit) *
|
345
|
+
// Add this small percentage to account for the APR on the debt between now and the transaction
|
346
|
+
0.0001
|
347
|
+
)
|
348
|
+
)
|
349
|
+
: toBaseUnit(usdToSwap / safeGetPrice(output.mint)!, output.decimals);
|
350
|
+
|
351
|
+
const exactOut =
|
352
|
+
targetLiqUtilizationRateBps === 0 || values.repayingCloseToMaxLtv;
|
350
353
|
const exactIn = !exactOut;
|
351
354
|
|
352
355
|
return {
|
@@ -354,18 +357,7 @@ export function getJupSwapRebalanceDetails(
|
|
354
357
|
outputMint: toWeb3JsPublicKey(output.mint),
|
355
358
|
destinationWallet: client.solautoPosition,
|
356
359
|
slippageIncFactor: 0.5 + (attemptNum ?? 0) * 0.2,
|
357
|
-
amount: exactOut
|
358
|
-
? outputAmount +
|
359
|
-
(targetLiqUtilizationRateBps === 0
|
360
|
-
? BigInt(
|
361
|
-
Math.round(
|
362
|
-
Number(outputAmount) *
|
363
|
-
// Add this small percentage to account for the APR on the debt between now and the transaction
|
364
|
-
0.0001
|
365
|
-
)
|
366
|
-
)
|
367
|
-
: BigInt(0))
|
368
|
-
: inputAmount,
|
360
|
+
amount: exactOut ? outputAmount : inputAmount,
|
369
361
|
exactIn: exactIn,
|
370
362
|
exactOut: exactOut,
|
371
363
|
};
|