@gearbox-protocol/sdk 3.0.0-next.127 → 3.0.0-next.129
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/lib/apy/convexAPY.js +1 -1
- package/lib/apy/curveAPY.d.ts +9 -2
- package/lib/apy/curveAPY.js +104 -68
- package/lib/apy/defiLamaAPY.js +5 -2
- package/lib/apy/yearnAPY.d.ts +2 -2
- package/lib/apy/yearnAPY.js +2 -1
- package/lib/core/creditAccount.d.ts +3 -1
- package/lib/core/creditAccount.js +63 -38
- package/lib/core/creditAccount.spec.js +184 -2
- package/lib/core/endpoint.d.ts +1 -0
- package/lib/core/endpoint.js +2 -0
- package/lib/core/rewardConvex.js +0 -2
- package/package.json +1 -1
package/lib/apy/convexAPY.js
CHANGED
|
@@ -168,7 +168,7 @@ function calculateConvexAPY(props) {
|
|
|
168
168
|
return finished ? 0n : extraAPY;
|
|
169
169
|
});
|
|
170
170
|
const extraAPYTotal = extraAPRs.reduce((acc, apy) => acc + apy, 0n);
|
|
171
|
-
const baseApy = props.curveAPY?.[crvToken]
|
|
171
|
+
const baseApy = props.curveAPY?.[crvToken]?.base || 0;
|
|
172
172
|
const apyTotal = crvAPY + cvxAPY + extraAPYTotal;
|
|
173
173
|
const apyTotalInPercent = apyTotal * sdk_gov_1.PERCENTAGE_DECIMALS * sdk_gov_1.PERCENTAGE_FACTOR;
|
|
174
174
|
const r = baseApy + Math.round(Number((apyTotalInPercent * 10n) / sdk_gov_1.WAD) / 10);
|
package/lib/apy/curveAPY.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NetworkType, PartialRecord } from "@gearbox-protocol/sdk-gov";
|
|
1
2
|
import { CurveLPToken } from "@gearbox-protocol/sdk-gov/lib/tokens/curveLP";
|
|
2
3
|
import { GearboxToken } from "@gearbox-protocol/sdk-gov/lib/tokens/gear";
|
|
3
4
|
interface CurvePoolData {
|
|
@@ -50,7 +51,13 @@ interface CurveAPY {
|
|
|
50
51
|
crv: number;
|
|
51
52
|
gauge: Array<[string, number]>;
|
|
52
53
|
}
|
|
53
|
-
export type CurveAPYResult =
|
|
54
|
-
export declare function getCurveAPY(): Promise<
|
|
54
|
+
export type CurveAPYResult = PartialRecord<CurveAPYTokens, CurveAPY>;
|
|
55
|
+
export declare function getCurveAPY(network: NetworkType): Promise<{
|
|
56
|
+
curveAPY: CurveAPYResult;
|
|
57
|
+
gearAPY: CurveAPY;
|
|
58
|
+
} | {
|
|
59
|
+
curveAPY?: undefined;
|
|
60
|
+
gearAPY?: undefined;
|
|
61
|
+
}>;
|
|
55
62
|
export declare function getCurveGearPool(): Promise<CurvePoolData | undefined>;
|
|
56
63
|
export {};
|
package/lib/apy/curveAPY.js
CHANGED
|
@@ -6,76 +6,66 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.getCurveGearPool = exports.getCurveAPY = void 0;
|
|
7
7
|
const sdk_gov_1 = require("@gearbox-protocol/sdk-gov");
|
|
8
8
|
const axios_1 = __importDefault(require("axios"));
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
crvPlain3andSUSD: "15",
|
|
15
|
-
steCRV: "14",
|
|
16
|
-
crvFRAX: "44",
|
|
17
|
-
GEAR: "factory-crypto-192",
|
|
18
|
-
OHMFRAXBP: "factory-crypto-158",
|
|
19
|
-
MIM_3LP3CRV: "40",
|
|
20
|
-
crvCRVETH: "crypto-3",
|
|
21
|
-
crvCVXETH: "crypto-4",
|
|
22
|
-
crvUSDTWBTCWETH: "factory-tricrypto-1",
|
|
23
|
-
LDOETH: "factory-crypto-204",
|
|
24
|
-
crvUSDUSDC: "factory-crvusd-0",
|
|
25
|
-
crvUSDUSDT: "factory-crvusd-1",
|
|
26
|
-
crvUSDFRAX: "factory-crvusd-4",
|
|
27
|
-
crvUSDETHCRV: "factory-tricrypto-4",
|
|
28
|
-
rETH_f: "factory-crypto-210",
|
|
29
|
-
wstETHCRV: "unknown",
|
|
30
|
-
"2CRV": "unknown",
|
|
31
|
-
"3c-crvUSD": "unknown",
|
|
32
|
-
crvUSDC: "unknown",
|
|
33
|
-
crvUSDT: "unknown",
|
|
34
|
-
crvUSDC_e: "unknown",
|
|
35
|
-
"3CRV": "unknown", // 0xEfDE221f306152971D8e9f181bFe998447975810
|
|
9
|
+
const GEAR_POOL = "0x5Be6C45e2d074fAa20700C49aDA3E88a1cc0025d".toLowerCase();
|
|
10
|
+
const CURVE_CHAINS = {
|
|
11
|
+
Arbitrum: "arbitrum",
|
|
12
|
+
Mainnet: "ethereum",
|
|
13
|
+
Optimism: "optimism",
|
|
36
14
|
};
|
|
37
|
-
// const CRYPTO = "https://api.curve.fi/api/getPools/
|
|
38
|
-
// const FACTORY = "https://api.curve.fi/api/getPools/
|
|
39
|
-
const CURVE_APY_URL = "https://www.convexfinance.com/api/curve-apys";
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
15
|
+
// const CRYPTO = "https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/crypto";
|
|
16
|
+
// const FACTORY = "https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/factory";
|
|
17
|
+
// const CURVE_APY_URL = "https://www.convexfinance.com/api/curve-apys";
|
|
18
|
+
const getVolumesURL = (n) => `https://api.curve.fi/api/getVolumes/${CURVE_CHAINS[n]}`;
|
|
19
|
+
const getMainURL = (n) => `https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/main`;
|
|
20
|
+
const getFactoryCryptoURL = (n) => `https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/factory-crypto`;
|
|
21
|
+
const getCryptoURL = (n) => `https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/crypto`;
|
|
22
|
+
const getFactoryTriCryptoURL = (n) => `https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/factory-tricrypto`;
|
|
23
|
+
const getFactoryCrvUsdURL = (n) => `https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/factory-crvusd`;
|
|
24
|
+
const getFactoryStableNgURL = (n) => `https://api.curve.fi/api/getPools/${CURVE_CHAINS[n]}/factory-stable-ng`;
|
|
25
|
+
async function getCurveAPY(network) {
|
|
46
26
|
try {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.map(resp => {
|
|
58
|
-
const { poolData = [] } = resp?.data?.data || {};
|
|
59
|
-
return poolData.map(p => [p.id, p]);
|
|
27
|
+
const currentTokens = sdk_gov_1.tokenDataByNetwork[network];
|
|
28
|
+
const { mainnetVolumes, mainnetFactoryPools, volumes, pools } = await getCurvePools(network);
|
|
29
|
+
const volumeByAddress = Object.fromEntries(volumes.data.data.pools.map(v => [v.address.toLowerCase(), v]));
|
|
30
|
+
const poolDataByAddress = Object.fromEntries(pools
|
|
31
|
+
.map(poolCategory => {
|
|
32
|
+
const { poolData = [] } = poolCategory?.data?.data || {};
|
|
33
|
+
return poolData.map((p) => [
|
|
34
|
+
p.lpTokenAddress.toLowerCase(),
|
|
35
|
+
p,
|
|
36
|
+
]);
|
|
60
37
|
})
|
|
61
38
|
.flat(1));
|
|
62
|
-
const curveAPY = sdk_gov_1.TypedObjectUtils.entries(
|
|
63
|
-
const
|
|
64
|
-
const pool =
|
|
65
|
-
const
|
|
66
|
-
const
|
|
39
|
+
const curveAPY = sdk_gov_1.TypedObjectUtils.entries(sdk_gov_1.curveTokens).reduce((acc, [curveSymbol]) => {
|
|
40
|
+
const address = (currentTokens?.[curveSymbol] || "").toLowerCase();
|
|
41
|
+
const pool = poolDataByAddress[address];
|
|
42
|
+
const volume = volumeByAddress[(pool?.address || "").toLowerCase()];
|
|
43
|
+
const baseAPY = volume?.latestDailyApyPcent || 0;
|
|
44
|
+
const maxCrv = Math.max(...(pool?.gaugeCrvApy || []), 0);
|
|
45
|
+
const extraRewards = (pool?.gaugeRewards || []).map(({ apy = 0, symbol }) => [
|
|
67
46
|
symbol.toLowerCase(),
|
|
68
47
|
curveAPYToBn(apy),
|
|
69
48
|
]);
|
|
70
|
-
const maxCrv = pool?.gaugeCrvApy?.length > 0 ? Math.max(...pool.gaugeCrvApy) : crvApy;
|
|
71
49
|
acc[curveSymbol] = {
|
|
72
|
-
base: curveAPYToBn(
|
|
73
|
-
crv: curveAPYToBn(maxCrv
|
|
50
|
+
base: curveAPYToBn(baseAPY),
|
|
51
|
+
crv: curveAPYToBn(maxCrv),
|
|
74
52
|
gauge: extraRewards,
|
|
75
53
|
};
|
|
76
54
|
return acc;
|
|
77
55
|
}, {});
|
|
78
|
-
|
|
56
|
+
const poolFactoryByAddress = Object.fromEntries((mainnetFactoryPools?.data?.data?.poolData || []).map((p) => [p.lpTokenAddress.toLowerCase(), p]));
|
|
57
|
+
const mainnetVolumeByAddress = Object.fromEntries(mainnetVolumes.data.data.pools.map(v => [v.address.toLowerCase(), v]));
|
|
58
|
+
const gearPool = poolFactoryByAddress[GEAR_POOL];
|
|
59
|
+
const gearVolume = mainnetVolumeByAddress[(gearPool?.address || "").toLowerCase()];
|
|
60
|
+
const gearAPY = {
|
|
61
|
+
base: curveAPYToBn(gearVolume?.latestDailyApyPcent || 0),
|
|
62
|
+
crv: curveAPYToBn(Math.max(...(gearPool?.gaugeCrvApy || []), 0)),
|
|
63
|
+
gauge: (gearPool?.gaugeRewards || []).map(({ apy = 0, symbol }) => [
|
|
64
|
+
symbol.toLowerCase(),
|
|
65
|
+
curveAPYToBn(apy),
|
|
66
|
+
]),
|
|
67
|
+
};
|
|
68
|
+
return { curveAPY, gearAPY };
|
|
79
69
|
}
|
|
80
70
|
catch (e) {
|
|
81
71
|
console.error(e);
|
|
@@ -83,20 +73,66 @@ async function getCurveAPY() {
|
|
|
83
73
|
}
|
|
84
74
|
}
|
|
85
75
|
exports.getCurveAPY = getCurveAPY;
|
|
76
|
+
async function getCurvePools(network) {
|
|
77
|
+
switch (network) {
|
|
78
|
+
case "Mainnet": {
|
|
79
|
+
const [volumes, mainnetFactoryPools, ...pools] = await Promise.all([
|
|
80
|
+
axios_1.default.get(getVolumesURL(network)),
|
|
81
|
+
axios_1.default.get(getFactoryCryptoURL(network)),
|
|
82
|
+
axios_1.default.get(getMainURL(network)),
|
|
83
|
+
axios_1.default.get(getCryptoURL(network)),
|
|
84
|
+
axios_1.default.get(getFactoryTriCryptoURL(network)),
|
|
85
|
+
axios_1.default.get(getFactoryCrvUsdURL(network)),
|
|
86
|
+
]);
|
|
87
|
+
return {
|
|
88
|
+
mainnetVolumes: volumes,
|
|
89
|
+
mainnetFactoryPools,
|
|
90
|
+
volumes,
|
|
91
|
+
pools: [mainnetFactoryPools, ...pools],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
case "Arbitrum": {
|
|
95
|
+
const [mainnetVolumes, mainnetFactoryPools, volumes, ...pools] = await Promise.all([
|
|
96
|
+
axios_1.default.get(getVolumesURL("Mainnet")),
|
|
97
|
+
axios_1.default.get(getFactoryCryptoURL("Mainnet")),
|
|
98
|
+
axios_1.default.get(getVolumesURL(network)),
|
|
99
|
+
axios_1.default.get(getMainURL(network)),
|
|
100
|
+
axios_1.default.get(getFactoryStableNgURL(network)),
|
|
101
|
+
]);
|
|
102
|
+
return {
|
|
103
|
+
mainnetVolumes,
|
|
104
|
+
mainnetFactoryPools,
|
|
105
|
+
volumes,
|
|
106
|
+
pools,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
case "Optimism": {
|
|
110
|
+
const [mainnetVolumes, mainnetFactoryPools, volumes, ...pools] = await Promise.all([
|
|
111
|
+
axios_1.default.get(getVolumesURL("Mainnet")),
|
|
112
|
+
axios_1.default.get(getFactoryCryptoURL("Mainnet")),
|
|
113
|
+
axios_1.default.get(getVolumesURL(network)),
|
|
114
|
+
axios_1.default.get(getMainURL(network)),
|
|
115
|
+
axios_1.default.get(getFactoryStableNgURL(network)),
|
|
116
|
+
]);
|
|
117
|
+
return {
|
|
118
|
+
mainnetVolumes,
|
|
119
|
+
mainnetFactoryPools,
|
|
120
|
+
volumes,
|
|
121
|
+
pools,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
throw new Error("Unknown network");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
86
128
|
function curveAPYToBn(baseApy) {
|
|
87
129
|
return Math.round(baseApy * Number(sdk_gov_1.PERCENTAGE_FACTOR));
|
|
88
130
|
}
|
|
89
131
|
async function getCurveGearPool() {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
]);
|
|
93
|
-
const
|
|
94
|
-
.map(resp => {
|
|
95
|
-
const { poolData = [] } = resp?.data?.data || {};
|
|
96
|
-
return poolData.map(p => [p.id, p]);
|
|
97
|
-
})
|
|
98
|
-
.flat(1));
|
|
99
|
-
const gearPool = poolDataByID[APY_DICTIONARY.GEAR];
|
|
132
|
+
const resp = await axios_1.default.get(getFactoryCryptoURL("Mainnet"));
|
|
133
|
+
const { poolData = [] } = resp?.data?.data || {};
|
|
134
|
+
const poolDataByAddress = Object.fromEntries(poolData.map(p => [p.lpTokenAddress.toLowerCase(), p]));
|
|
135
|
+
const gearPool = poolDataByAddress[GEAR_POOL];
|
|
100
136
|
return gearPool;
|
|
101
137
|
}
|
|
102
138
|
exports.getCurveGearPool = getCurveGearPool;
|
package/lib/apy/defiLamaAPY.js
CHANGED
|
@@ -10,7 +10,10 @@ const LAMA_URL = "https://charts-server.fly.dev/api/defillama?ids=";
|
|
|
10
10
|
async function getDefiLamaAPY(networkType) {
|
|
11
11
|
try {
|
|
12
12
|
const currentNormal = NORMAL_TO_LAMA[networkType];
|
|
13
|
-
const
|
|
13
|
+
const idList = Object.values(currentNormal);
|
|
14
|
+
if (idList.length === 0)
|
|
15
|
+
return {};
|
|
16
|
+
const res = await axios_1.default.get(`${LAMA_URL}${idList.join(",")}`);
|
|
14
17
|
const itemsRecord = res.data.data.reduce((acc, item) => {
|
|
15
18
|
acc[item.pool] = item;
|
|
16
19
|
return acc;
|
|
@@ -34,7 +37,7 @@ const NORMAL_TO_LAMA = {
|
|
|
34
37
|
osETH: "4d01599c-69ae-41a3-bae1-5fab896f04c8",
|
|
35
38
|
auraB_rETH_STABLE_vault: "a4b5b995-99e7-4b8f-916d-8940b5627d70",
|
|
36
39
|
},
|
|
37
|
-
Optimism: {
|
|
40
|
+
Optimism: {},
|
|
38
41
|
Arbitrum: {},
|
|
39
42
|
};
|
|
40
43
|
// const CONVEX_TO_LAMA: Record<
|
package/lib/apy/yearnAPY.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { NetworkType, YearnLPToken } from "@gearbox-protocol/sdk-gov";
|
|
2
|
-
export type YearnAPYResult =
|
|
1
|
+
import { NetworkType, PartialRecord, YearnLPToken } from "@gearbox-protocol/sdk-gov";
|
|
2
|
+
export type YearnAPYResult = PartialRecord<YearnLPToken, number>;
|
|
3
3
|
export declare function getYearnAPY(network: NetworkType): Promise<YearnAPYResult>;
|
package/lib/apy/yearnAPY.js
CHANGED
|
@@ -10,13 +10,14 @@ const getUrl = (chainId) => `https://ydaemon.yearn.finance/vaults/all?chainids=$
|
|
|
10
10
|
async function getYearnAPY(network) {
|
|
11
11
|
try {
|
|
12
12
|
const chainId = sdk_gov_1.CHAINS[network];
|
|
13
|
+
const currentTokens = sdk_gov_1.tokenDataByNetwork[network];
|
|
13
14
|
const { data } = await axios_1.default.get(getUrl(chainId));
|
|
14
15
|
const dataByAddress = data.reduce((acc, d) => {
|
|
15
16
|
acc[d.address.toLowerCase()] = d;
|
|
16
17
|
return acc;
|
|
17
18
|
}, {});
|
|
18
19
|
const yearnAPY = sdk_gov_1.TypedObjectUtils.entries(sdk_gov_1.yearnTokens).reduce((acc, [yearnSymbol]) => {
|
|
19
|
-
const address = (
|
|
20
|
+
const address = (currentTokens?.[yearnSymbol] || "").toLowerCase();
|
|
20
21
|
const data = dataByAddress[address];
|
|
21
22
|
const { apr: apy } = data || {};
|
|
22
23
|
const { netAPR } = apy || {};
|
|
@@ -40,6 +40,7 @@ export interface CalcQuotaUpdateProps {
|
|
|
40
40
|
initialQuotas: Record<string, Pick<CaTokenBalance, "quota">>;
|
|
41
41
|
liquidationThresholds: Record<string, bigint>;
|
|
42
42
|
assetsAfterUpdate: Record<string, AssetWithAmountInTarget>;
|
|
43
|
+
maxDebt: bigint;
|
|
43
44
|
allowedToSpend: Record<string, {}>;
|
|
44
45
|
allowedToObtain: Record<string, {}>;
|
|
45
46
|
quotaReserve: bigint;
|
|
@@ -120,7 +121,8 @@ export declare class CreditAccountData {
|
|
|
120
121
|
static calcHealthFactor({ assets, quotas, quotasInfo, liquidationThresholds, underlyingToken, debt, prices, }: CalcHealthFactorProps): number;
|
|
121
122
|
static calcRecommendedQuota({ amount, debt, lt, quotaReserve, }: CalcRecommendedQuotaProps): bigint;
|
|
122
123
|
static calcDefaultQuota({ amount, lt, quotaReserve }: CalcDefaultQuotaProps): bigint;
|
|
123
|
-
static calcQuotaUpdate(
|
|
124
|
+
static calcQuotaUpdate(props: CalcQuotaUpdateProps): CalcQuotaUpdateReturnType;
|
|
125
|
+
private static getSingleQuotaChange;
|
|
124
126
|
static calcQuotaBorrowRate({ quotas, quotaRates }: CalcQuotaBorrowRateProps): bigint;
|
|
125
127
|
static calcRelativeBaseBorrowRate({ debt, baseRateWithFee, assetAmountInUnderlying, totalValue, }: CalcRelativeBaseBorrowRateProps): bigint;
|
|
126
128
|
static liquidationPrice({ liquidationThresholds, debt, underlyingToken, targetToken, assets, }: LiquidationPriceProps): bigint;
|
|
@@ -239,56 +239,81 @@ class CreditAccountData {
|
|
|
239
239
|
sdk_gov_1.PERCENTAGE_FACTOR;
|
|
240
240
|
return recommendedQuota;
|
|
241
241
|
}
|
|
242
|
-
static calcQuotaUpdate(
|
|
243
|
-
const
|
|
242
|
+
static calcQuotaUpdate(props) {
|
|
243
|
+
const { quotas, initialQuotas, maxDebt, allowedToSpend, allowedToObtain } = props;
|
|
244
|
+
const quotaDecrease = Object.keys(allowedToSpend).reduce((acc, token) => {
|
|
245
|
+
const ch = CreditAccountData.getSingleQuotaChange(token, 0n, props);
|
|
246
|
+
if (ch)
|
|
247
|
+
acc[ch.token] = ch;
|
|
248
|
+
return acc;
|
|
249
|
+
}, {});
|
|
250
|
+
const quotaCap = maxDebt * 2n;
|
|
251
|
+
const quotaBought = Object.values(initialQuotas).reduce((sum, q) => sum + (q?.quota || 0n), 0n);
|
|
252
|
+
const quotaReduced = Object.values(quotaDecrease).reduce((sum, q) => sum + (q.balance || 0n), 0n);
|
|
253
|
+
const maxQuotaIncrease = math_1.BigIntMath.max(quotaCap - (quotaBought + quotaReduced), 0n);
|
|
254
|
+
const quotaIncrease = Object.keys(allowedToObtain).reduce((acc, token) => {
|
|
255
|
+
const ch = CreditAccountData.getSingleQuotaChange(token, maxQuotaIncrease, props);
|
|
256
|
+
if (ch)
|
|
257
|
+
acc[ch.token] = ch;
|
|
258
|
+
return acc;
|
|
259
|
+
}, {});
|
|
260
|
+
const quotaChange = {
|
|
261
|
+
...quotaDecrease,
|
|
262
|
+
...quotaIncrease,
|
|
263
|
+
};
|
|
264
|
+
const desiredQuota = Object.values(quotas).reduce((acc, cmQuota) => {
|
|
244
265
|
const { token, isActive } = cmQuota;
|
|
245
266
|
const { quota: initialQuota = 0n } = initialQuotas[token] || {};
|
|
246
267
|
if (!isActive) {
|
|
247
|
-
acc
|
|
268
|
+
acc[token] = {
|
|
248
269
|
balance: initialQuota,
|
|
249
270
|
token,
|
|
250
271
|
};
|
|
251
|
-
return acc;
|
|
252
|
-
}
|
|
253
|
-
// min(debt,assetAmountInUnderlying*LT)*(1+buffer)
|
|
254
|
-
const after = assetsAfterUpdate[token];
|
|
255
|
-
const { amountInTarget = 0n } = after || {};
|
|
256
|
-
const lt = liquidationThresholds[token] || 0n;
|
|
257
|
-
const desiredQuota = this.calcDefaultQuota({
|
|
258
|
-
lt,
|
|
259
|
-
quotaReserve,
|
|
260
|
-
amount: amountInTarget,
|
|
261
|
-
});
|
|
262
|
-
const quotaChange = desiredQuota - initialQuota;
|
|
263
|
-
const correctIncrease = after && allowedToObtain[token] && quotaChange > 0;
|
|
264
|
-
const correctDecrease = after && allowedToSpend[token] && quotaChange < 0;
|
|
265
|
-
if (correctIncrease || correctDecrease) {
|
|
266
|
-
acc.desiredQuota[token] = {
|
|
267
|
-
balance: desiredQuota,
|
|
268
|
-
token,
|
|
269
|
-
};
|
|
270
272
|
}
|
|
271
273
|
else {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
+
const change = quotaChange[token]?.balance || 0n;
|
|
275
|
+
const quotaAfter = initialQuota + change;
|
|
276
|
+
acc[token] = {
|
|
277
|
+
balance: quotaAfter,
|
|
274
278
|
token,
|
|
275
279
|
};
|
|
276
280
|
}
|
|
277
|
-
if (correctIncrease) {
|
|
278
|
-
acc.quotaIncrease.push({
|
|
279
|
-
balance: quotaChange,
|
|
280
|
-
token,
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
if (correctDecrease) {
|
|
284
|
-
acc.quotaDecrease.push({
|
|
285
|
-
balance: quotaChange,
|
|
286
|
-
token,
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
281
|
return acc;
|
|
290
|
-
}, {
|
|
291
|
-
return
|
|
282
|
+
}, {});
|
|
283
|
+
return {
|
|
284
|
+
desiredQuota,
|
|
285
|
+
quotaDecrease: Object.values(quotaDecrease),
|
|
286
|
+
quotaIncrease: Object.values(quotaIncrease),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
static getSingleQuotaChange(token, maxQuotaIncrease, props) {
|
|
290
|
+
const { isActive = false } = props.quotas[token] || {};
|
|
291
|
+
const { quota: initialQuota = 0n } = props.initialQuotas[token] || {};
|
|
292
|
+
if (!isActive) {
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
// min(debt,assetAmountInUnderlying*LT)*(1+buffer)
|
|
296
|
+
const assetAfter = props.assetsAfterUpdate[token];
|
|
297
|
+
const { amountInTarget = 0n } = assetAfter || {};
|
|
298
|
+
const lt = props.liquidationThresholds[token] || 0n;
|
|
299
|
+
const defaultQuota = this.calcDefaultQuota({
|
|
300
|
+
lt,
|
|
301
|
+
quotaReserve: props.quotaReserve,
|
|
302
|
+
amount: amountInTarget,
|
|
303
|
+
});
|
|
304
|
+
const unsafeQuotaChange = defaultQuota - initialQuota;
|
|
305
|
+
const quotaChange = unsafeQuotaChange > 0
|
|
306
|
+
? math_1.BigIntMath.min(maxQuotaIncrease, unsafeQuotaChange)
|
|
307
|
+
: unsafeQuotaChange;
|
|
308
|
+
const correctIncrease = assetAfter && props.allowedToObtain[token] && quotaChange > 0;
|
|
309
|
+
const correctDecrease = assetAfter && props.allowedToSpend[token] && quotaChange < 0;
|
|
310
|
+
if (correctIncrease || correctDecrease) {
|
|
311
|
+
return {
|
|
312
|
+
balance: quotaChange,
|
|
313
|
+
token,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return undefined;
|
|
292
317
|
}
|
|
293
318
|
static calcQuotaBorrowRate({ quotas, quotaRates }) {
|
|
294
319
|
const totalRateBalance = Object.values(quotas).reduce((acc, { token, balance }) => {
|
|
@@ -422,9 +422,11 @@ const DEFAULT_LT = {
|
|
|
422
422
|
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: sdk_gov_1.PERCENTAGE_FACTOR,
|
|
423
423
|
[sdk_gov_1.tokenDataByNetwork.Mainnet.STETH]: sdk_gov_1.PERCENTAGE_FACTOR,
|
|
424
424
|
};
|
|
425
|
+
const HUGE_MAX_DEBT = 20n;
|
|
425
426
|
describe("CreditAccount calcQuotaUpdate test", () => {
|
|
426
427
|
it("open account should buy quota", () => {
|
|
427
428
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
429
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
428
430
|
quotaReserve: QUOTA_RESERVE,
|
|
429
431
|
quotas: cmQuotas,
|
|
430
432
|
initialQuotas: {},
|
|
@@ -475,6 +477,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
475
477
|
});
|
|
476
478
|
it("add collateral should buy quota", () => {
|
|
477
479
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
480
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
478
481
|
quotaReserve: QUOTA_RESERVE,
|
|
479
482
|
quotas: cmQuotas,
|
|
480
483
|
initialQuotas: caQuota,
|
|
@@ -515,6 +518,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
515
518
|
});
|
|
516
519
|
it("add collateral should add additional quota", () => {
|
|
517
520
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
521
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
518
522
|
quotaReserve: QUOTA_RESERVE,
|
|
519
523
|
quotas: cmQuotas,
|
|
520
524
|
initialQuotas: caQuota,
|
|
@@ -555,6 +559,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
555
559
|
});
|
|
556
560
|
it("add collateral shouldn't add additional quota", () => {
|
|
557
561
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
562
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
558
563
|
quotaReserve: QUOTA_RESERVE,
|
|
559
564
|
quotas: cmQuotas,
|
|
560
565
|
initialQuotas: caQuota,
|
|
@@ -590,6 +595,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
590
595
|
});
|
|
591
596
|
it("swap should buy quota", () => {
|
|
592
597
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
598
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
593
599
|
quotaReserve: QUOTA_RESERVE,
|
|
594
600
|
quotas: cmQuotas,
|
|
595
601
|
initialQuotas: caQuota,
|
|
@@ -635,8 +641,9 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
635
641
|
},
|
|
636
642
|
});
|
|
637
643
|
});
|
|
638
|
-
it("swap should
|
|
644
|
+
it("swap should buy additional quota", () => {
|
|
639
645
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
646
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
640
647
|
quotaReserve: QUOTA_RESERVE,
|
|
641
648
|
quotas: cmQuotas,
|
|
642
649
|
initialQuotas: caQuota,
|
|
@@ -687,8 +694,9 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
687
694
|
},
|
|
688
695
|
});
|
|
689
696
|
});
|
|
690
|
-
it("swap shouldn't
|
|
697
|
+
it("swap shouldn't buy additional quota", () => {
|
|
691
698
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
699
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
692
700
|
quotaReserve: QUOTA_RESERVE,
|
|
693
701
|
quotas: cmQuotas,
|
|
694
702
|
initialQuotas: caQuota,
|
|
@@ -734,6 +742,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
734
742
|
});
|
|
735
743
|
it("shouldn't change quota if disallowed", () => {
|
|
736
744
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
745
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
737
746
|
quotaReserve: QUOTA_RESERVE,
|
|
738
747
|
quotas: cmQuotas,
|
|
739
748
|
initialQuotas: caQuota,
|
|
@@ -772,6 +781,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
772
781
|
});
|
|
773
782
|
it("shouldn't change quota if it is disabled", () => {
|
|
774
783
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
784
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
775
785
|
quotaReserve: QUOTA_RESERVE,
|
|
776
786
|
quotas: {
|
|
777
787
|
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
@@ -827,6 +837,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
827
837
|
});
|
|
828
838
|
it("swap shouldn't buy quota if no lt", () => {
|
|
829
839
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
840
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
830
841
|
quotaReserve: QUOTA_RESERVE,
|
|
831
842
|
quotas: cmQuotas,
|
|
832
843
|
initialQuotas: {
|
|
@@ -874,6 +885,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
874
885
|
});
|
|
875
886
|
it("swap should buy quota with respect to lt", () => {
|
|
876
887
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
888
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
877
889
|
quotaReserve: QUOTA_RESERVE,
|
|
878
890
|
quotas: cmQuotas,
|
|
879
891
|
initialQuotas: {
|
|
@@ -927,6 +939,7 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
927
939
|
});
|
|
928
940
|
it("swap shouldn't buy quota with respect to lt", () => {
|
|
929
941
|
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
942
|
+
maxDebt: HUGE_MAX_DEBT,
|
|
930
943
|
quotaReserve: QUOTA_RESERVE,
|
|
931
944
|
quotas: cmQuotas,
|
|
932
945
|
initialQuotas: {
|
|
@@ -973,6 +986,175 @@ describe("CreditAccount calcQuotaUpdate test", () => {
|
|
|
973
986
|
},
|
|
974
987
|
});
|
|
975
988
|
});
|
|
989
|
+
it("swap should buy additional quota after limit was increased", () => {
|
|
990
|
+
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
991
|
+
maxDebt: 10n,
|
|
992
|
+
quotaReserve: QUOTA_RESERVE,
|
|
993
|
+
quotas: cmQuotas,
|
|
994
|
+
initialQuotas: {
|
|
995
|
+
...caQuota,
|
|
996
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
997
|
+
quota: 10n,
|
|
998
|
+
},
|
|
999
|
+
},
|
|
1000
|
+
assetsAfterUpdate: {
|
|
1001
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1002
|
+
amountInTarget: 20n,
|
|
1003
|
+
balance: 0n,
|
|
1004
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1005
|
+
},
|
|
1006
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {
|
|
1007
|
+
amountInTarget: 0n,
|
|
1008
|
+
balance: 0n,
|
|
1009
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1010
|
+
},
|
|
1011
|
+
},
|
|
1012
|
+
allowedToObtain: {
|
|
1013
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {},
|
|
1014
|
+
},
|
|
1015
|
+
allowedToSpend: {
|
|
1016
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {},
|
|
1017
|
+
},
|
|
1018
|
+
liquidationThresholds: DEFAULT_LT,
|
|
1019
|
+
});
|
|
1020
|
+
(0, chai_1.expect)(result.quotaIncrease).to.be.deep.eq([
|
|
1021
|
+
{
|
|
1022
|
+
balance: 10n,
|
|
1023
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1024
|
+
},
|
|
1025
|
+
]);
|
|
1026
|
+
(0, chai_1.expect)(result.quotaDecrease).to.be.deep.eq([
|
|
1027
|
+
{
|
|
1028
|
+
balance: -10n,
|
|
1029
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1030
|
+
},
|
|
1031
|
+
]);
|
|
1032
|
+
(0, chai_1.expect)(result.desiredQuota).to.be.deep.eq({
|
|
1033
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1034
|
+
balance: 20n,
|
|
1035
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1036
|
+
},
|
|
1037
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {
|
|
1038
|
+
balance: 0n,
|
|
1039
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1040
|
+
},
|
|
1041
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.STETH]: {
|
|
1042
|
+
balance: 0n,
|
|
1043
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.STETH,
|
|
1044
|
+
},
|
|
1045
|
+
});
|
|
1046
|
+
});
|
|
1047
|
+
it("swap should buy additional quota with respect to debt limit", () => {
|
|
1048
|
+
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
1049
|
+
maxDebt: 9n,
|
|
1050
|
+
quotaReserve: QUOTA_RESERVE,
|
|
1051
|
+
quotas: cmQuotas,
|
|
1052
|
+
initialQuotas: {
|
|
1053
|
+
...caQuota,
|
|
1054
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1055
|
+
quota: 10n,
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
assetsAfterUpdate: {
|
|
1059
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1060
|
+
amountInTarget: 20n,
|
|
1061
|
+
balance: 0n,
|
|
1062
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1063
|
+
},
|
|
1064
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {
|
|
1065
|
+
amountInTarget: 0n,
|
|
1066
|
+
balance: 0n,
|
|
1067
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1068
|
+
},
|
|
1069
|
+
},
|
|
1070
|
+
allowedToObtain: {
|
|
1071
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {},
|
|
1072
|
+
},
|
|
1073
|
+
allowedToSpend: {
|
|
1074
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {},
|
|
1075
|
+
},
|
|
1076
|
+
liquidationThresholds: DEFAULT_LT,
|
|
1077
|
+
});
|
|
1078
|
+
(0, chai_1.expect)(result.quotaIncrease).to.be.deep.eq([
|
|
1079
|
+
{
|
|
1080
|
+
balance: 8n,
|
|
1081
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1082
|
+
},
|
|
1083
|
+
]);
|
|
1084
|
+
(0, chai_1.expect)(result.quotaDecrease).to.be.deep.eq([
|
|
1085
|
+
{
|
|
1086
|
+
balance: -10n,
|
|
1087
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1088
|
+
},
|
|
1089
|
+
]);
|
|
1090
|
+
(0, chai_1.expect)(result.desiredQuota).to.be.deep.eq({
|
|
1091
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1092
|
+
balance: 18n,
|
|
1093
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1094
|
+
},
|
|
1095
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {
|
|
1096
|
+
balance: 0n,
|
|
1097
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1098
|
+
},
|
|
1099
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.STETH]: {
|
|
1100
|
+
balance: 0n,
|
|
1101
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.STETH,
|
|
1102
|
+
},
|
|
1103
|
+
});
|
|
1104
|
+
});
|
|
1105
|
+
it("swap shouldn't buy additional quota if debt limit more then current quota", () => {
|
|
1106
|
+
const result = creditAccount_1.CreditAccountData.calcQuotaUpdate({
|
|
1107
|
+
maxDebt: 5n,
|
|
1108
|
+
quotaReserve: QUOTA_RESERVE,
|
|
1109
|
+
quotas: cmQuotas,
|
|
1110
|
+
initialQuotas: {
|
|
1111
|
+
...caQuota,
|
|
1112
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1113
|
+
quota: 10n,
|
|
1114
|
+
},
|
|
1115
|
+
},
|
|
1116
|
+
assetsAfterUpdate: {
|
|
1117
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1118
|
+
amountInTarget: 20n,
|
|
1119
|
+
balance: 0n,
|
|
1120
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1121
|
+
},
|
|
1122
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {
|
|
1123
|
+
amountInTarget: 0n,
|
|
1124
|
+
balance: 0n,
|
|
1125
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1126
|
+
},
|
|
1127
|
+
},
|
|
1128
|
+
allowedToObtain: {
|
|
1129
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {},
|
|
1130
|
+
},
|
|
1131
|
+
allowedToSpend: {
|
|
1132
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {},
|
|
1133
|
+
},
|
|
1134
|
+
liquidationThresholds: DEFAULT_LT,
|
|
1135
|
+
});
|
|
1136
|
+
(0, chai_1.expect)(result.quotaIncrease).to.be.deep.eq([]);
|
|
1137
|
+
(0, chai_1.expect)(result.quotaDecrease).to.be.deep.eq([
|
|
1138
|
+
{
|
|
1139
|
+
balance: -10n,
|
|
1140
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1141
|
+
},
|
|
1142
|
+
]);
|
|
1143
|
+
(0, chai_1.expect)(result.desiredQuota).to.be.deep.eq({
|
|
1144
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.DAI]: {
|
|
1145
|
+
balance: 10n,
|
|
1146
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.DAI,
|
|
1147
|
+
},
|
|
1148
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.WETH]: {
|
|
1149
|
+
balance: 0n,
|
|
1150
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.WETH,
|
|
1151
|
+
},
|
|
1152
|
+
[sdk_gov_1.tokenDataByNetwork.Mainnet.STETH]: {
|
|
1153
|
+
balance: 0n,
|
|
1154
|
+
token: sdk_gov_1.tokenDataByNetwork.Mainnet.STETH,
|
|
1155
|
+
},
|
|
1156
|
+
});
|
|
1157
|
+
});
|
|
976
1158
|
});
|
|
977
1159
|
describe("CreditAccount calcAvgQuotaBorrowRate test", () => {
|
|
978
1160
|
it("should calculate quota rate (same amounts, different rates)", () => {
|
package/lib/core/endpoint.d.ts
CHANGED
package/lib/core/endpoint.js
CHANGED
|
@@ -5,6 +5,7 @@ const sdk_gov_1 = require("@gearbox-protocol/sdk-gov");
|
|
|
5
5
|
exports.TESTNET_CHAINS = {
|
|
6
6
|
Mainnet: 7878,
|
|
7
7
|
Optimism: 7879,
|
|
8
|
+
Arbitrum: 7880,
|
|
8
9
|
};
|
|
9
10
|
const CHARTS_BACKEND_ADDRESSES = {
|
|
10
11
|
[sdk_gov_1.CHAINS.Mainnet]: "https://charts-server.fly.dev",
|
|
@@ -12,6 +13,7 @@ const CHARTS_BACKEND_ADDRESSES = {
|
|
|
12
13
|
[exports.TESTNET_CHAINS.Mainnet]: "https://testnet.gearbox.foundation",
|
|
13
14
|
// !& test server for optimism
|
|
14
15
|
[exports.TESTNET_CHAINS.Optimism]: "https://testnet.gearbox.foundation",
|
|
16
|
+
[exports.TESTNET_CHAINS.Arbitrum]: "https://testnet.gearbox.foundation",
|
|
15
17
|
};
|
|
16
18
|
class ChartsApi {
|
|
17
19
|
static getUrl = (url, chainId, options, priceSource) => [
|
package/lib/core/rewardConvex.js
CHANGED
|
@@ -10,8 +10,6 @@ const types_1 = require("../types");
|
|
|
10
10
|
class RewardConvex {
|
|
11
11
|
static poolInterface = types_1.IBaseRewardPool__factory.createInterface();
|
|
12
12
|
static async findRewards(ca, cm, network, provider) {
|
|
13
|
-
if (network !== "Mainnet")
|
|
14
|
-
return [];
|
|
15
13
|
const { auraCalls, auraDistribution, convexCalls, convexDistribution } = RewardConvex.prepareMultiCalls(ca.addr, cm, network);
|
|
16
14
|
const auraTotal = auraCalls.flat(1);
|
|
17
15
|
const convexTotal = convexCalls.flat(1);
|