@strkfarm/sdk 2.0.0-dev.3 → 2.0.0-dev.30
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/cli.js +190 -36
- package/dist/cli.mjs +188 -34
- package/dist/index.browser.global.js +78478 -45620
- package/dist/index.browser.mjs +19583 -9901
- package/dist/index.d.ts +3763 -1424
- package/dist/index.js +20980 -11063
- package/dist/index.mjs +20948 -11087
- package/package.json +1 -1
- package/src/data/avnu.abi.json +840 -0
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/dataTypes/_bignumber.ts +13 -4
- package/src/dataTypes/bignumber.browser.ts +6 -1
- package/src/dataTypes/bignumber.node.ts +5 -1
- package/src/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +76 -41
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +175 -3
- package/src/modules/ExtendedWrapperSDk/types.ts +28 -5
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +275 -59
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +4 -4
- package/src/modules/ekubo-pricer.ts +79 -0
- package/src/modules/ekubo-quoter.ts +48 -30
- package/src/modules/erc20.ts +17 -0
- package/src/modules/harvests.ts +43 -29
- package/src/modules/pragma.ts +23 -8
- package/src/modules/pricer-from-api.ts +156 -15
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +40 -4
- package/src/modules/pricerBase.ts +2 -1
- package/src/node/deployer.ts +36 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/strategies/base-strategy.ts +78 -10
- package/src/strategies/ekubo-cl-vault.tsx +906 -347
- package/src/strategies/factory.ts +159 -0
- package/src/strategies/index.ts +7 -1
- package/src/strategies/registry.ts +239 -0
- package/src/strategies/sensei.ts +335 -7
- package/src/strategies/svk-strategy.ts +97 -27
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +2 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +180 -265
- package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
- package/src/strategies/universal-adapters/common-adapter.ts +206 -203
- package/src/strategies/universal-adapters/extended-adapter.ts +490 -316
- package/src/strategies/universal-adapters/index.ts +11 -8
- package/src/strategies/universal-adapters/svk-troves-adapter.ts +364 -0
- package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
- package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +200 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +120 -82
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +476 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +1067 -704
- package/src/strategies/universal-adapters/vesu-position-common.ts +251 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
- package/src/strategies/universal-lst-muliplier-strategy.tsx +397 -204
- package/src/strategies/universal-strategy.tsx +1426 -1173
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +2233 -0
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +4087 -0
- package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +783 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +38 -16
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +88 -0
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +1 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +5 -6
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +259 -103
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +688 -817
- package/src/strategies/vesu-rebalance.tsx +255 -152
- package/src/utils/cacheClass.ts +11 -2
- package/src/utils/health-factor-math.ts +4 -1
- package/src/utils/index.ts +3 -1
- package/src/utils/logger.browser.ts +22 -4
- package/src/utils/logger.node.ts +259 -24
- package/src/utils/starknet-call-parser.ts +1036 -0
- package/src/utils/strategy-utils.ts +61 -0
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BaseAdapter,
|
|
3
|
+
DepositParams,
|
|
4
|
+
WithdrawParams,
|
|
5
|
+
BaseAdapterConfig,
|
|
6
|
+
} from "./baseAdapter";
|
|
2
7
|
import { Protocols } from "@/interfaces";
|
|
3
8
|
import { SupportedPosition } from "./baseAdapter";
|
|
4
9
|
import { PositionAPY, APYType, PositionAmount } from "./baseAdapter";
|
|
@@ -9,9 +14,13 @@ import { ContractAddr } from "@/dataTypes";
|
|
|
9
14
|
import { AVNU_EXCHANGE_FOR_LEGACY_USDC } from "./adapter-utils";
|
|
10
15
|
import { StandardMerkleTree } from "@/utils";
|
|
11
16
|
import { hash, uint256 } from "starknet";
|
|
12
|
-
import { Global } from "@/global";
|
|
13
17
|
import { AdapterLeafType, GenerateCallFn } from "./baseAdapter";
|
|
14
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
AVNU_LEGACY_SANITIZER,
|
|
20
|
+
EXTENDED_SANITIZER,
|
|
21
|
+
SIMPLE_SANITIZER,
|
|
22
|
+
toBigInt,
|
|
23
|
+
} from "./adapter-utils";
|
|
15
24
|
import ExtendedWrapper, {
|
|
16
25
|
AssetOperationStatus,
|
|
17
26
|
AssetOperationType,
|
|
@@ -24,17 +33,21 @@ import { Balance, OpenOrder, OrderStatus } from "@/modules/ExtendedWrapperSDk";
|
|
|
24
33
|
import axios from "axios";
|
|
25
34
|
import { AvnuAdapter } from "./avnu-adapter";
|
|
26
35
|
import { logger } from "@/utils";
|
|
36
|
+
import { TokenInfo } from "@/interfaces";
|
|
37
|
+
|
|
27
38
|
export interface ExtendedAdapterConfig extends BaseAdapterConfig {
|
|
28
39
|
vaultIdExtended: number;
|
|
29
40
|
extendedContract: ContractAddr; //0x04270219d365d6b017231b52e92b3fb5d7c8378b05e9abc97724537a80e93b0f
|
|
30
|
-
|
|
31
|
-
|
|
41
|
+
extendedBackendReadUrl: string;
|
|
42
|
+
extendedBackendWriteUrl: string;
|
|
32
43
|
extendedTimeout: number;
|
|
33
44
|
extendedRetries: number;
|
|
34
45
|
extendedBaseUrl: string;
|
|
35
46
|
extendedMarketName: string;
|
|
36
47
|
extendedPrecision: number;
|
|
37
48
|
avnuAdapter: AvnuAdapter;
|
|
49
|
+
retryDelayForOrderStatus: number;
|
|
50
|
+
minimumExtendedMovementAmount: number;
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
export class ExtendedAdapter extends BaseAdapter<
|
|
@@ -43,40 +56,80 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
43
56
|
> {
|
|
44
57
|
readonly config: ExtendedAdapterConfig;
|
|
45
58
|
readonly client: ExtendedWrapper;
|
|
59
|
+
readonly usdcToken: TokenInfo;
|
|
60
|
+
readonly retryDelayForOrderStatus: number;
|
|
61
|
+
readonly minimumExtendedMovementAmount: number;
|
|
46
62
|
|
|
47
63
|
constructor(config: ExtendedAdapterConfig) {
|
|
48
64
|
super(config, ExtendedAdapter.name, Protocols.EXTENDED);
|
|
49
65
|
this.config = config as ExtendedAdapterConfig;
|
|
50
66
|
const client = new ExtendedWrapper({
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
writeUrl: this.config.extendedBackendWriteUrl,
|
|
68
|
+
readUrl: this.config.extendedBackendReadUrl,
|
|
69
|
+
apiKey: "", // todo add auth
|
|
53
70
|
timeout: this.config.extendedTimeout,
|
|
54
71
|
retries: this.config.extendedRetries,
|
|
55
72
|
});
|
|
73
|
+
this.minimumExtendedMovementAmount =
|
|
74
|
+
this.config.minimumExtendedMovementAmount ?? 5; //5 usdc
|
|
56
75
|
this.client = client;
|
|
76
|
+
this.retryDelayForOrderStatus =
|
|
77
|
+
this.config.retryDelayForOrderStatus ?? 3000;
|
|
78
|
+
this.usdcToken = this.config.supportedPositions[0].asset;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private _depositApproveProofReadableId(): string {
|
|
82
|
+
return `extended_approve_${this.usdcToken.symbol}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private _depositCallProofReadableId(): string {
|
|
86
|
+
return `extended_deposit_${this.usdcToken.symbol}`;
|
|
57
87
|
}
|
|
58
88
|
//abstract means the method has no implementation in this class; instead, child classes must implement it.
|
|
59
89
|
protected async getAPY(
|
|
60
|
-
supportedPosition: SupportedPosition
|
|
90
|
+
supportedPosition: SupportedPosition,
|
|
61
91
|
): Promise<PositionAPY> {
|
|
62
92
|
/** Considering supportedPosiiton.isDebt as side parameter to get the funding rates */
|
|
63
93
|
const side = supportedPosition.isDebt ? "LONG" : "SHORT";
|
|
64
|
-
const fundingRates = await this.
|
|
65
|
-
if (fundingRates.
|
|
94
|
+
const fundingRates = await this.getFundingRates(side);
|
|
95
|
+
if (!fundingRates.success) {
|
|
66
96
|
logger.error("error getting funding rates", fundingRates);
|
|
67
97
|
return { apy: 0, type: APYType.BASE };
|
|
68
98
|
}
|
|
69
|
-
const fundingRate:FundingRate = fundingRates.data[0];
|
|
99
|
+
const fundingRate: FundingRate = fundingRates.data[0];
|
|
70
100
|
const apy = Number(fundingRate.f) * 365 * 24;
|
|
71
101
|
return { apy: apy, type: APYType.BASE };
|
|
72
102
|
}
|
|
73
103
|
|
|
104
|
+
public async getFundingRates(
|
|
105
|
+
side: string,
|
|
106
|
+
startTime?: number,
|
|
107
|
+
endTime?: number,
|
|
108
|
+
): Promise<{ success: boolean; data: FundingRate[] }> {
|
|
109
|
+
try {
|
|
110
|
+
const response = await this.client.getFundingRates(
|
|
111
|
+
this.config.extendedMarketName,
|
|
112
|
+
side,
|
|
113
|
+
startTime,
|
|
114
|
+
endTime,
|
|
115
|
+
);
|
|
116
|
+
if (response.status !== "OK") {
|
|
117
|
+
logger.error("error getting funding rates", response.data);
|
|
118
|
+
return { success: false, data: [] };
|
|
119
|
+
}
|
|
120
|
+
logger.debug(
|
|
121
|
+
`success getting funding rates market=${this.config.extendedMarketName} side=${side} count=${response.data.length}`,
|
|
122
|
+
);
|
|
123
|
+
return { success: true, data: response.data };
|
|
124
|
+
} catch (err) {
|
|
125
|
+
logger.error("error getting funding rates", err);
|
|
126
|
+
return { success: false, data: [] };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
74
130
|
protected async getPosition(
|
|
75
|
-
supportedPosition: SupportedPosition
|
|
131
|
+
supportedPosition: SupportedPosition,
|
|
76
132
|
): Promise<PositionAmount> {
|
|
77
|
-
if (!this.client) {
|
|
78
|
-
throw new Error("Client not initialized");
|
|
79
|
-
}
|
|
80
133
|
const holdings = await this.getExtendedDepositAmount();
|
|
81
134
|
if (!holdings) {
|
|
82
135
|
throw new Error("No position found");
|
|
@@ -85,7 +138,7 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
85
138
|
const amount = holdings.equity;
|
|
86
139
|
return Promise.resolve({
|
|
87
140
|
amount: new Web3Number(amount, 0),
|
|
88
|
-
remarks:
|
|
141
|
+
remarks: `Extended Equity`,
|
|
89
142
|
});
|
|
90
143
|
}
|
|
91
144
|
|
|
@@ -118,29 +171,12 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
118
171
|
sanitizer: ContractAddr;
|
|
119
172
|
id: string;
|
|
120
173
|
}[] {
|
|
121
|
-
const usdceToken = Global.getDefaultTokens().find(token => token.symbol === "USDCe");
|
|
122
174
|
return [
|
|
123
175
|
{
|
|
124
|
-
target: this.
|
|
125
|
-
method: "approve",
|
|
126
|
-
packedArguments: [
|
|
127
|
-
AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt(),
|
|
128
|
-
],
|
|
129
|
-
id: `extended_approve_${this.config.supportedPositions[0].asset.symbol}`,
|
|
130
|
-
sanitizer: AVNU_LEGACY_SANITIZER,
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
target: AVNU_EXCHANGE_FOR_LEGACY_USDC,
|
|
134
|
-
method: "swap_to_legacy",
|
|
135
|
-
packedArguments: [],
|
|
136
|
-
id: `extended_swap_to_legacy_${this.config.supportedPositions[0].asset.symbol}`,
|
|
137
|
-
sanitizer: AVNU_LEGACY_SANITIZER,
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
target: usdceToken!.address,
|
|
176
|
+
target: this.usdcToken.address,
|
|
141
177
|
method: "approve",
|
|
142
178
|
packedArguments: [this.config.extendedContract.toBigInt()],
|
|
143
|
-
id:
|
|
179
|
+
id: this._depositApproveProofReadableId(),
|
|
144
180
|
sanitizer: SIMPLE_SANITIZER,
|
|
145
181
|
},
|
|
146
182
|
{
|
|
@@ -148,55 +184,11 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
148
184
|
method: "deposit",
|
|
149
185
|
packedArguments: [BigInt(this.config.vaultIdExtended)],
|
|
150
186
|
sanitizer: EXTENDED_SANITIZER,
|
|
151
|
-
id:
|
|
187
|
+
id: this._depositCallProofReadableId(),
|
|
152
188
|
},
|
|
153
189
|
];
|
|
154
190
|
}
|
|
155
191
|
|
|
156
|
-
getSwapFromLegacyLeaf(): AdapterLeafType<DepositParams> {
|
|
157
|
-
const leafConfigs = this._getSwapFromLegacyLeaf();
|
|
158
|
-
const leaves = leafConfigs.map(config => {
|
|
159
|
-
const { target, method, packedArguments, sanitizer, id } = config;
|
|
160
|
-
const leaf = this.constructSimpleLeafData({
|
|
161
|
-
id: id,
|
|
162
|
-
target,
|
|
163
|
-
method,
|
|
164
|
-
packedArguments
|
|
165
|
-
}, sanitizer);
|
|
166
|
-
return leaf;
|
|
167
|
-
});
|
|
168
|
-
return { leaves, callConstructor: this.getSwapFromLegacyCall.bind(this) as unknown as GenerateCallFn<DepositParams> };
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
protected _getSwapFromLegacyLeaf(): {
|
|
172
|
-
target: ContractAddr;
|
|
173
|
-
method: string;
|
|
174
|
-
packedArguments: bigint[];
|
|
175
|
-
sanitizer: ContractAddr;
|
|
176
|
-
id: string;
|
|
177
|
-
}[] {
|
|
178
|
-
const usdceToken = Global.getDefaultTokens().find(token => token.symbol === "USDCe");
|
|
179
|
-
return [
|
|
180
|
-
{
|
|
181
|
-
target: usdceToken!.address,
|
|
182
|
-
method: "approve",
|
|
183
|
-
packedArguments: [
|
|
184
|
-
AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt(),
|
|
185
|
-
],
|
|
186
|
-
id: `extendedswaplegacyapprove_${usdceToken!.symbol}`,
|
|
187
|
-
sanitizer: AVNU_LEGACY_SANITIZER,
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
target: AVNU_EXCHANGE_FOR_LEGACY_USDC,
|
|
191
|
-
method: "swap_to_new",
|
|
192
|
-
packedArguments: [],
|
|
193
|
-
id: `extended_swap_to_new_${usdceToken!.symbol}`,
|
|
194
|
-
sanitizer: AVNU_LEGACY_SANITIZER,
|
|
195
|
-
},
|
|
196
|
-
]
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
192
|
protected _getWithdrawLeaf(): {
|
|
201
193
|
target: ContractAddr;
|
|
202
194
|
method: string;
|
|
@@ -212,54 +204,14 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
212
204
|
|
|
213
205
|
async getDepositCall(params: DepositParams): Promise<ManageCall[]> {
|
|
214
206
|
try {
|
|
215
|
-
const
|
|
216
|
-
const
|
|
217
|
-
const salt = Math.floor(
|
|
218
|
-
Math.random() * 10 ** usdcToken.decimals
|
|
219
|
-
);
|
|
220
|
-
// Give approval for more amount than the required amount
|
|
221
|
-
const amount = uint256.bnToUint256(params.amount.multipliedBy(10).toWei());
|
|
222
|
-
const quotes = await this.config.avnuAdapter.getQuotesAvnu(
|
|
223
|
-
usdcToken.address.toString(),
|
|
224
|
-
usdceToken!.address.toString(),
|
|
225
|
-
params.amount.toNumber(),
|
|
226
|
-
this.config.avnuAdapter.config.vaultAllocator.address.toString(),
|
|
227
|
-
usdceToken!.decimals,
|
|
228
|
-
false
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
if (!quotes) {
|
|
232
|
-
logger.error("error getting quotes from avnu");
|
|
233
|
-
return [];
|
|
234
|
-
}
|
|
235
|
-
const getCalldata = await this.config.avnuAdapter.getSwapCallData(quotes!);
|
|
236
|
-
const swapCallData = getCalldata[0];
|
|
237
|
-
//change extended sanitizer here
|
|
207
|
+
const salt = Math.floor(Math.random() * 10 ** this.usdcToken.decimals);
|
|
208
|
+
const amount = uint256.bnToUint256(params.amount.toWei());
|
|
238
209
|
return [
|
|
239
210
|
{
|
|
240
|
-
|
|
241
|
-
call: {
|
|
242
|
-
contractAddress: usdcToken.address,
|
|
243
|
-
selector: hash.getSelectorFromName("approve"),
|
|
244
|
-
calldata: [
|
|
245
|
-
AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt(),
|
|
246
|
-
toBigInt(amount.low.toString()), // amount low
|
|
247
|
-
toBigInt(amount.high.toString()), // amount high
|
|
248
|
-
],
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
{
|
|
252
|
-
sanitizer: AVNU_LEGACY_SANITIZER,
|
|
253
|
-
call: {
|
|
254
|
-
contractAddress: AVNU_EXCHANGE_FOR_LEGACY_USDC,
|
|
255
|
-
selector: hash.getSelectorFromName("swap_to_legacy"),
|
|
256
|
-
calldata: swapCallData,
|
|
257
|
-
},
|
|
258
|
-
},
|
|
259
|
-
{
|
|
211
|
+
proofReadableId: this._depositApproveProofReadableId(),
|
|
260
212
|
sanitizer: SIMPLE_SANITIZER,
|
|
261
213
|
call: {
|
|
262
|
-
contractAddress:
|
|
214
|
+
contractAddress: this.usdcToken.address,
|
|
263
215
|
selector: hash.getSelectorFromName("approve"),
|
|
264
216
|
calldata: [
|
|
265
217
|
this.config.extendedContract.toBigInt(),
|
|
@@ -269,6 +221,7 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
269
221
|
},
|
|
270
222
|
},
|
|
271
223
|
{
|
|
224
|
+
proofReadableId: this._depositCallProofReadableId(),
|
|
272
225
|
sanitizer: EXTENDED_SANITIZER,
|
|
273
226
|
call: {
|
|
274
227
|
contractAddress: this.config.extendedContract,
|
|
@@ -287,84 +240,8 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
287
240
|
}
|
|
288
241
|
}
|
|
289
242
|
|
|
290
|
-
getProofsForFromLegacySwap<T>(tree: StandardMerkleTree): { proofs: string[][], callConstructor: GenerateCallFn<DepositParams> | GenerateCallFn<WithdrawParams> } {
|
|
291
|
-
let proofGroups: string[][] = [];
|
|
292
|
-
|
|
293
|
-
const ids = this.getSwapFromLegacyLeaf().leaves.map(l => l.readableId)
|
|
294
|
-
// console.log(`${this.name}::getProofs ids: ${ids}`);
|
|
295
|
-
for (const [i, v] of tree.entries()) {
|
|
296
|
-
// console.log(`${this.name}::getProofs v: ${v.readableId}`);
|
|
297
|
-
if (ids.includes(v.readableId)) {
|
|
298
|
-
//console.log(`${this.name}::getProofs found id: ${v.readableId}`);
|
|
299
|
-
proofGroups.push(tree.getProof(i));
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (proofGroups.length != ids.length) {
|
|
303
|
-
throw new Error(`Not all proofs found for IDs: ${ids.join(', ')}`);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// find leaf adapter
|
|
307
|
-
return {
|
|
308
|
-
proofs: proofGroups,
|
|
309
|
-
callConstructor: this.getSwapFromLegacyCall.bind(this)
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
async getSwapFromLegacyCall(params: DepositParams): Promise<ManageCall[]> {
|
|
314
|
-
try {
|
|
315
|
-
const usdcToken = this.config.supportedPositions[0].asset;
|
|
316
|
-
const usdceToken = Global.getDefaultTokens().find(token => token.symbol === "USDCe");
|
|
317
|
-
// Give approval for more amount than the required amount
|
|
318
|
-
const amount = uint256.bnToUint256(params.amount.multipliedBy(10).toWei());
|
|
319
|
-
const quotes = await this.config.avnuAdapter.getQuotesAvnu(
|
|
320
|
-
usdceToken!.address.toString(),
|
|
321
|
-
usdcToken!.address.toString(),
|
|
322
|
-
params.amount.toNumber(),
|
|
323
|
-
this.config.avnuAdapter.config.vaultAllocator.address.toString(),
|
|
324
|
-
usdcToken!.decimals,
|
|
325
|
-
false
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
if (!quotes) {
|
|
329
|
-
logger.error("error getting quotes from avnu");
|
|
330
|
-
return [];
|
|
331
|
-
}
|
|
332
|
-
const getCalldata = await this.config.avnuAdapter.getSwapCallData(quotes!);
|
|
333
|
-
const swapCallData = getCalldata[0];
|
|
334
|
-
//change extended sanitizer here
|
|
335
|
-
return [
|
|
336
|
-
{
|
|
337
|
-
sanitizer: AVNU_LEGACY_SANITIZER,
|
|
338
|
-
call: {
|
|
339
|
-
contractAddress: usdceToken!.address,
|
|
340
|
-
selector: hash.getSelectorFromName("approve"),
|
|
341
|
-
calldata: [
|
|
342
|
-
AVNU_EXCHANGE_FOR_LEGACY_USDC.toBigInt(),
|
|
343
|
-
toBigInt(amount.low.toString()), // amount low
|
|
344
|
-
toBigInt(amount.high.toString()), // amount high
|
|
345
|
-
],
|
|
346
|
-
},
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
sanitizer: AVNU_LEGACY_SANITIZER,
|
|
350
|
-
call: {
|
|
351
|
-
contractAddress: AVNU_EXCHANGE_FOR_LEGACY_USDC,
|
|
352
|
-
selector: hash.getSelectorFromName("swap_to_new"),
|
|
353
|
-
calldata: swapCallData,
|
|
354
|
-
},
|
|
355
|
-
}
|
|
356
|
-
];
|
|
357
|
-
} catch (error) {
|
|
358
|
-
logger.error(`Error creating Deposit Call: ${error}`);
|
|
359
|
-
return [];
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
//Swap wbtc to usdc
|
|
363
243
|
async getWithdrawCall(params: WithdrawParams): Promise<ManageCall[]> {
|
|
364
244
|
try {
|
|
365
|
-
if (!this.client) {
|
|
366
|
-
throw new Error("Client not initialized");
|
|
367
|
-
}
|
|
368
245
|
return [];
|
|
369
246
|
} catch (error) {
|
|
370
247
|
logger.error(`Error creating Withdraw Call: ${error}`);
|
|
@@ -372,20 +249,105 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
372
249
|
}
|
|
373
250
|
}
|
|
374
251
|
|
|
375
|
-
async withdrawFromExtended(amount: Web3Number): Promise<
|
|
252
|
+
async withdrawFromExtended(amount: Web3Number): Promise<{
|
|
253
|
+
status: boolean;
|
|
254
|
+
receivedTxnHash: boolean;
|
|
255
|
+
}> {
|
|
376
256
|
try {
|
|
377
257
|
if (!this.client) {
|
|
378
|
-
|
|
258
|
+
logger.error("Client not initialized");
|
|
259
|
+
return {
|
|
260
|
+
status: false,
|
|
261
|
+
receivedTxnHash: false,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (amount.lessThanOrEqualTo(0)) {
|
|
265
|
+
logger.error(
|
|
266
|
+
`Invalid withdrawal amount: ${amount.toNumber()}. Amount must be positive.`,
|
|
267
|
+
);
|
|
268
|
+
return {
|
|
269
|
+
status: false,
|
|
270
|
+
receivedTxnHash: false,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (amount.lessThanOrEqualTo(this.minimumExtendedMovementAmount)) {
|
|
274
|
+
logger.warn(
|
|
275
|
+
`Withdrawal amount ${amount.toNumber()} is below minimum Extended movement amount ${this.minimumExtendedMovementAmount}. Skipping withdrawal.`,
|
|
276
|
+
);
|
|
277
|
+
return {
|
|
278
|
+
status: false,
|
|
279
|
+
receivedTxnHash: false,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const holdings = await this.getExtendedDepositAmount();
|
|
283
|
+
if (!holdings) {
|
|
284
|
+
logger.error(
|
|
285
|
+
"Cannot get holdings - unable to validate withdrawal amount",
|
|
286
|
+
);
|
|
287
|
+
return {
|
|
288
|
+
status: false,
|
|
289
|
+
receivedTxnHash: false,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const availableForWithdrawal = parseFloat(
|
|
294
|
+
holdings.availableForWithdrawal,
|
|
295
|
+
);
|
|
296
|
+
if (
|
|
297
|
+
!Number.isFinite(availableForWithdrawal) ||
|
|
298
|
+
availableForWithdrawal < 0
|
|
299
|
+
) {
|
|
300
|
+
logger.error(
|
|
301
|
+
`Invalid availableForWithdrawal: ${holdings.availableForWithdrawal}. Expected a finite, non-negative number.`,
|
|
302
|
+
);
|
|
303
|
+
return {
|
|
304
|
+
status: false,
|
|
305
|
+
receivedTxnHash: false,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const withdrawalAmount = amount.toNumber();
|
|
310
|
+
if (withdrawalAmount > availableForWithdrawal) {
|
|
311
|
+
logger.error(
|
|
312
|
+
`Withdrawal amount ${withdrawalAmount} exceeds available balance ${availableForWithdrawal}`,
|
|
313
|
+
);
|
|
314
|
+
return {
|
|
315
|
+
status: false,
|
|
316
|
+
receivedTxnHash: false,
|
|
317
|
+
};
|
|
379
318
|
}
|
|
380
|
-
|
|
319
|
+
|
|
320
|
+
logger.info(
|
|
321
|
+
`Withdrawing ${withdrawalAmount} from Extended. Available balance: ${availableForWithdrawal}`,
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const withdrawalRequest = await this.client.withdrawUSDC(
|
|
325
|
+
amount.toFixed(2),
|
|
326
|
+
);
|
|
327
|
+
|
|
381
328
|
if (withdrawalRequest.status === "OK") {
|
|
382
|
-
const withdrawalStatus = await this.getDepositOrWithdrawalStatus(
|
|
383
|
-
|
|
329
|
+
const withdrawalStatus = await this.getDepositOrWithdrawalStatus(
|
|
330
|
+
withdrawalRequest.data,
|
|
331
|
+
AssetOperationType.WITHDRAWAL,
|
|
332
|
+
);
|
|
333
|
+
return {
|
|
334
|
+
status: true,
|
|
335
|
+
receivedTxnHash: withdrawalStatus,
|
|
336
|
+
};
|
|
384
337
|
}
|
|
385
|
-
|
|
338
|
+
logger.error(
|
|
339
|
+
`Withdrawal request failed with status: ${withdrawalRequest.status}`,
|
|
340
|
+
);
|
|
341
|
+
return {
|
|
342
|
+
status: false,
|
|
343
|
+
receivedTxnHash: false,
|
|
344
|
+
};
|
|
386
345
|
} catch (error) {
|
|
387
346
|
logger.error(`Error creating Withdraw Call: ${error}`);
|
|
388
|
-
return
|
|
347
|
+
return {
|
|
348
|
+
status: false,
|
|
349
|
+
receivedTxnHash: false,
|
|
350
|
+
};
|
|
389
351
|
}
|
|
390
352
|
}
|
|
391
353
|
|
|
@@ -394,21 +356,37 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
394
356
|
}
|
|
395
357
|
|
|
396
358
|
async getExtendedDepositAmount(): Promise<Balance | undefined> {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
359
|
+
const failResult = {
|
|
360
|
+
collateral_name: "",
|
|
361
|
+
balance: "0",
|
|
362
|
+
equity: "0",
|
|
363
|
+
availableForTrade: "0",
|
|
364
|
+
availableForWithdrawal: "0",
|
|
365
|
+
unrealisedPnl: "0",
|
|
366
|
+
initialMargin: "0",
|
|
367
|
+
marginRatio: "0",
|
|
368
|
+
updatedTime: Date.now(),
|
|
369
|
+
};
|
|
370
|
+
const fail = (message: string) => {
|
|
371
|
+
logger.error(message);
|
|
372
|
+
return failResult;
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
try {
|
|
376
|
+
const result = await this.client.getHoldings();
|
|
377
|
+
if (result.status !== "OK") {
|
|
378
|
+
return fail("error getting holdings - API returned null/undefined");
|
|
379
|
+
}
|
|
380
|
+
const holdings = result.data;
|
|
381
|
+
if (!holdings) {
|
|
382
|
+
return fail(
|
|
383
|
+
"holdings data is null/undefined - treating as zero balance",
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
return holdings;
|
|
387
|
+
} catch (error) {
|
|
388
|
+
return fail(`error getting holdings - exception: ${error}`);
|
|
410
389
|
}
|
|
411
|
-
return holdings;
|
|
412
390
|
}
|
|
413
391
|
|
|
414
392
|
async setLeverage(leverage: string, marketName: string): Promise<boolean> {
|
|
@@ -427,64 +405,77 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
427
405
|
}
|
|
428
406
|
|
|
429
407
|
async getAllOpenPositions(): Promise<Position[] | null> {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
} else {
|
|
441
|
-
return response.data;
|
|
408
|
+
try {
|
|
409
|
+
const response = await this.client.getPositions(
|
|
410
|
+
this.config.extendedMarketName,
|
|
411
|
+
);
|
|
412
|
+
if (response.status === "OK") {
|
|
413
|
+
if (response.data.length === 0) {
|
|
414
|
+
return [];
|
|
415
|
+
} else {
|
|
416
|
+
return response.data;
|
|
417
|
+
}
|
|
442
418
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
async getOrderHistory(marketName: string): Promise<OpenOrder[] | null> {
|
|
448
|
-
if (this.client === null) {
|
|
449
|
-
logger.error("error initializing client");
|
|
419
|
+
return null;
|
|
420
|
+
} catch (err) {
|
|
421
|
+
logger.error("error getting all open positions", err);
|
|
450
422
|
return null;
|
|
451
423
|
}
|
|
452
|
-
const result = await this.client.getOrderHistory(marketName);
|
|
453
|
-
return result.data;
|
|
454
424
|
}
|
|
455
425
|
|
|
426
|
+
// async getOrderHistory(marketName: string): Promise<OpenOrder[] | null> {
|
|
427
|
+
// try {
|
|
428
|
+
// const result = await axios.get<ApiResponse<OpenOrder[]>>(`${this.config.extendedBackendReadUrl}/marketOrders/${marketName}`);
|
|
429
|
+
// if (!result.data.success) {
|
|
430
|
+
// return null;
|
|
431
|
+
// }
|
|
432
|
+
// return result.data.data;
|
|
433
|
+
// } catch (err) {
|
|
434
|
+
// logger.error("error getting order history", err);
|
|
435
|
+
// return null;
|
|
436
|
+
// }
|
|
437
|
+
// }
|
|
438
|
+
|
|
456
439
|
async getOrderStatus(
|
|
457
440
|
orderId: string,
|
|
458
|
-
marketName: string
|
|
441
|
+
marketName: string,
|
|
459
442
|
): Promise<OpenOrder | null> {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
443
|
+
try {
|
|
444
|
+
if (this.client === null) {
|
|
445
|
+
logger.error("error initializing client");
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
const orderhistoryResponse =
|
|
449
|
+
await this.client.getOrderHistory(marketName);
|
|
465
450
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
451
|
+
if (
|
|
452
|
+
orderhistoryResponse.status !== "OK" ||
|
|
453
|
+
orderhistoryResponse.data.length === 0
|
|
454
|
+
) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
const orderhistory = orderhistoryResponse.data;
|
|
458
|
+
const order = orderhistory
|
|
459
|
+
.slice(0, 20)
|
|
460
|
+
.find((order) => order.id.toString() === orderId);
|
|
461
|
+
if (order) {
|
|
462
|
+
return order;
|
|
463
|
+
}
|
|
464
|
+
return null; // Order not found
|
|
465
|
+
} catch (error) {
|
|
466
|
+
logger.error(`error getting order status: ${error}`);
|
|
475
467
|
return null;
|
|
476
468
|
}
|
|
477
|
-
return order;
|
|
478
469
|
}
|
|
479
470
|
|
|
480
|
-
async
|
|
471
|
+
async fetchOrderBookFromExtended(): Promise<{
|
|
481
472
|
status: boolean;
|
|
482
473
|
bid: Web3Number;
|
|
483
474
|
ask: Web3Number;
|
|
484
475
|
}> {
|
|
485
476
|
try {
|
|
486
477
|
const res = await axios.get(
|
|
487
|
-
`${this.config.extendedBaseUrl}/api/v1/info/markets/${this.config.extendedMarketName}/orderbook
|
|
478
|
+
`${this.config.extendedBaseUrl}/api/v1/info/markets/${this.config.extendedMarketName}/orderbook`,
|
|
488
479
|
);
|
|
489
480
|
let ask_price = new Web3Number(0, 0);
|
|
490
481
|
let bid_price = new Web3Number(0, 0);
|
|
@@ -502,12 +493,12 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
502
493
|
bid_price = new Web3Number(bid.price, 0);
|
|
503
494
|
|
|
504
495
|
return {
|
|
505
|
-
status:
|
|
496
|
+
status: true,
|
|
506
497
|
bid: bid_price, // check for quantity as well, not at the moment though
|
|
507
498
|
ask: ask_price, // check for quantity as well, not at the moment though
|
|
508
499
|
};
|
|
509
500
|
} catch (err) {
|
|
510
|
-
logger.error(`
|
|
501
|
+
logger.error(`extended orderbook fetch failed: ${err}`);
|
|
511
502
|
return {
|
|
512
503
|
status: false,
|
|
513
504
|
bid: new Web3Number(0, 0),
|
|
@@ -521,33 +512,77 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
521
512
|
btcAmount: number,
|
|
522
513
|
side: OrderSide,
|
|
523
514
|
attempt = 1,
|
|
524
|
-
maxAttempts = 5
|
|
515
|
+
maxAttempts = 5,
|
|
525
516
|
): Promise<{ position_id: string; btc_exposure: string } | null> {
|
|
526
517
|
try {
|
|
527
518
|
if (this.client === null) {
|
|
528
519
|
logger.error("error initializing client");
|
|
529
520
|
return null;
|
|
530
521
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
522
|
+
// todo, instead get levgerage and log errors if not ideal lever already
|
|
523
|
+
// const setLeverage = await this.setLeverage(
|
|
524
|
+
// leverage,
|
|
525
|
+
// this.config.extendedMarketName,
|
|
526
|
+
// );
|
|
527
|
+
// if (!setLeverage) {
|
|
528
|
+
// logger.error("error depositing or setting leverage");
|
|
529
|
+
// return null;
|
|
530
|
+
// }
|
|
531
|
+
const { ask, bid } = await this.fetchOrderBookFromExtended();
|
|
532
|
+
if (
|
|
533
|
+
!ask ||
|
|
534
|
+
!bid ||
|
|
535
|
+
ask.lessThanOrEqualTo(0) ||
|
|
536
|
+
bid.lessThanOrEqualTo(0)
|
|
537
|
+
) {
|
|
538
|
+
logger.error(
|
|
539
|
+
`Invalid orderbook prices: ask=${ask?.toNumber()}, bid=${bid?.toNumber()}`,
|
|
540
|
+
);
|
|
541
541
|
return null;
|
|
542
542
|
}
|
|
543
|
-
|
|
543
|
+
|
|
544
544
|
const spread = ask.minus(bid);
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
545
|
+
const midPrice = ask.plus(bid).div(2);
|
|
546
|
+
/** Maximum deviation: 50% of spread (to prevent extremely unfavorable prices) */
|
|
547
|
+
const MAX_PRICE_DEVIATION_MULTIPLIER = 0.5;
|
|
548
|
+
const priceAdjustmentMultiplier = Math.min(
|
|
549
|
+
0.2 * attempt,
|
|
550
|
+
MAX_PRICE_DEVIATION_MULTIPLIER,
|
|
551
|
+
);
|
|
552
|
+
const priceAdjustment = spread.times(priceAdjustmentMultiplier);
|
|
553
|
+
|
|
554
|
+
let price = midPrice;
|
|
555
|
+
if (side === OrderSide.SELL) {
|
|
556
|
+
price = midPrice.minus(priceAdjustment);
|
|
557
|
+
} else {
|
|
558
|
+
price = midPrice.plus(priceAdjustment);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/** Validate price is still reasonable (within 50% of mid price) */
|
|
562
|
+
const maxDeviation = midPrice.times(0.5);
|
|
563
|
+
if (price.minus(midPrice).abs().greaterThan(maxDeviation)) {
|
|
564
|
+
logger.error(
|
|
565
|
+
`Price deviation too large on attempt ${attempt}: price=${price.toNumber()}, midPrice=${midPrice.toNumber()}, deviation=${price
|
|
566
|
+
.minus(midPrice)
|
|
567
|
+
.abs()
|
|
568
|
+
.toNumber()}`,
|
|
569
|
+
);
|
|
570
|
+
if (attempt >= maxAttempts) {
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
price =
|
|
574
|
+
side === OrderSide.SELL
|
|
575
|
+
? midPrice.minus(maxDeviation)
|
|
576
|
+
: midPrice.plus(maxDeviation);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
logger.info(
|
|
580
|
+
`createOrder attempt ${attempt}/${maxAttempts}: side=${side}, midPrice=${midPrice.toNumber()}, adjustedPrice=${price.toNumber()}, adjustment=${priceAdjustmentMultiplier * 100
|
|
581
|
+
}%`,
|
|
582
|
+
);
|
|
583
|
+
|
|
549
584
|
const amount_in_token = (btcAmount * parseInt(leverage)).toFixed(
|
|
550
|
-
this.config.extendedPrecision
|
|
585
|
+
this.config.extendedPrecision,
|
|
551
586
|
); // gives the amount of wbtc
|
|
552
587
|
|
|
553
588
|
const result = await this.createExtendedPositon(
|
|
@@ -555,16 +590,68 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
555
590
|
this.config.extendedMarketName,
|
|
556
591
|
amount_in_token,
|
|
557
592
|
price.toFixed(0),
|
|
558
|
-
side
|
|
593
|
+
side,
|
|
559
594
|
);
|
|
560
|
-
if (!result) {
|
|
595
|
+
if (!result || !result.position_id) {
|
|
596
|
+
logger.error("Failed to create order - no position_id returned");
|
|
561
597
|
return null;
|
|
562
598
|
}
|
|
563
|
-
|
|
564
|
-
const
|
|
565
|
-
|
|
599
|
+
|
|
600
|
+
const positionId = result.position_id;
|
|
601
|
+
logger.info(
|
|
602
|
+
`Order created with position_id: ${positionId}. Waiting for API to update...`,
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
let openOrder = await this.getOrderStatus(
|
|
606
|
+
positionId,
|
|
607
|
+
this.config.extendedMarketName,
|
|
608
|
+
);
|
|
609
|
+
const maxStatusRetries = 3;
|
|
610
|
+
const statusRetryDelay = 5000;
|
|
611
|
+
|
|
612
|
+
if (!openOrder) {
|
|
613
|
+
logger.warn(
|
|
614
|
+
`Order ${positionId} not found in API yet. Retrying status fetch (max ${maxStatusRetries} times)...`,
|
|
615
|
+
);
|
|
616
|
+
for (
|
|
617
|
+
let statusRetry = 1;
|
|
618
|
+
statusRetry <= maxStatusRetries;
|
|
619
|
+
statusRetry++
|
|
620
|
+
) {
|
|
621
|
+
await new Promise((resolve) => setTimeout(resolve, statusRetryDelay));
|
|
622
|
+
openOrder = await this.getOrderStatus(
|
|
623
|
+
positionId,
|
|
624
|
+
this.config.extendedMarketName,
|
|
625
|
+
);
|
|
626
|
+
if (openOrder) {
|
|
627
|
+
logger.info(
|
|
628
|
+
`Order ${positionId} found after ${statusRetry} status retry(ies)`,
|
|
629
|
+
);
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
logger.warn(
|
|
633
|
+
`Order ${positionId} still not found after ${statusRetry}/${maxStatusRetries} status retries`,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (openOrder && openOrder.status === OrderStatus.FILLED) {
|
|
639
|
+
logger.info(
|
|
640
|
+
`Order ${positionId} successfully filled with quantity ${openOrder.qty}`,
|
|
641
|
+
);
|
|
642
|
+
return {
|
|
643
|
+
position_id: positionId,
|
|
644
|
+
btc_exposure: openOrder.qty,
|
|
645
|
+
};
|
|
646
|
+
} else if (openOrder && openOrder.status !== OrderStatus.FILLED) {
|
|
647
|
+
// Order found but NOT FILLED - retry (recreate)
|
|
648
|
+
logger.warn(
|
|
649
|
+
`Order ${positionId} found but status is ${openOrder.status}, not FILLED. Retrying order creation...`,
|
|
650
|
+
);
|
|
566
651
|
if (attempt >= maxAttempts) {
|
|
567
|
-
logger.error(
|
|
652
|
+
logger.error(
|
|
653
|
+
`Max retries reached — order ${positionId} status is ${openOrder.status}, not FILLED`,
|
|
654
|
+
);
|
|
568
655
|
return null;
|
|
569
656
|
} else {
|
|
570
657
|
const backoff = 2000 * attempt;
|
|
@@ -574,17 +661,22 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
574
661
|
btcAmount,
|
|
575
662
|
side,
|
|
576
663
|
attempt + 1,
|
|
577
|
-
maxAttempts
|
|
664
|
+
maxAttempts,
|
|
578
665
|
);
|
|
579
666
|
}
|
|
580
667
|
} else {
|
|
668
|
+
logger.warn(
|
|
669
|
+
`Order ${positionId} not found in API after ${maxStatusRetries} status retries (API update delayed ~30s). We got position_id from creation, so order exists. Returning position_id - status will be checked in next loop iteration.`,
|
|
670
|
+
);
|
|
581
671
|
return {
|
|
582
|
-
position_id:
|
|
583
|
-
btc_exposure:
|
|
672
|
+
position_id: positionId,
|
|
673
|
+
btc_exposure: amount_in_token,
|
|
584
674
|
};
|
|
585
675
|
}
|
|
586
676
|
} catch (err: any) {
|
|
587
|
-
logger.error(
|
|
677
|
+
logger.error(
|
|
678
|
+
`createShortOrder failed on attempt ${attempt}: ${err.message}`,
|
|
679
|
+
);
|
|
588
680
|
|
|
589
681
|
if (attempt < maxAttempts) {
|
|
590
682
|
const backoff = 1200 * attempt;
|
|
@@ -595,7 +687,7 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
595
687
|
btcAmount,
|
|
596
688
|
side,
|
|
597
689
|
attempt + 1,
|
|
598
|
-
maxAttempts
|
|
690
|
+
maxAttempts,
|
|
599
691
|
);
|
|
600
692
|
}
|
|
601
693
|
logger.error("Max retry attempts reached — aborting createShortOrder");
|
|
@@ -608,19 +700,21 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
608
700
|
marketName: string,
|
|
609
701
|
amount: string,
|
|
610
702
|
price: string,
|
|
611
|
-
side: OrderSide
|
|
703
|
+
side: OrderSide,
|
|
612
704
|
) {
|
|
613
705
|
try {
|
|
614
706
|
const result =
|
|
615
707
|
side === OrderSide.SELL
|
|
616
708
|
? await client.createSellOrder(marketName, amount, price, {
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
709
|
+
post_only: false,
|
|
710
|
+
reduce_only: false,
|
|
711
|
+
time_in_force: TimeInForce.IOC,
|
|
712
|
+
})
|
|
620
713
|
: await client.createBuyOrder(marketName, amount, price, {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
714
|
+
post_only: false,
|
|
715
|
+
reduce_only: true,
|
|
716
|
+
time_in_force: TimeInForce.IOC,
|
|
717
|
+
});
|
|
624
718
|
if (result.data.id) {
|
|
625
719
|
const position_id = result.data.id.toString();
|
|
626
720
|
return {
|
|
@@ -634,28 +728,108 @@ export class ExtendedAdapter extends BaseAdapter<
|
|
|
634
728
|
}
|
|
635
729
|
}
|
|
636
730
|
|
|
637
|
-
async getDepositOrWithdrawalStatus(
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
731
|
+
async getDepositOrWithdrawalStatus(
|
|
732
|
+
orderId: number | string, // for deposits, send txn hash as string
|
|
733
|
+
operationsType: AssetOperationType,
|
|
734
|
+
): Promise<boolean> {
|
|
735
|
+
const maxAttempts = 15;
|
|
736
|
+
const retryDelayMs = 30000; // 60 seconds
|
|
737
|
+
|
|
738
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
739
|
+
try {
|
|
740
|
+
let transferHistory = await this.client.getAssetOperations({
|
|
741
|
+
operationsType: [operationsType],
|
|
742
|
+
operationsStatus: [AssetOperationStatus.COMPLETED],
|
|
743
|
+
});
|
|
744
|
+
if (operationsType === AssetOperationType.DEPOSIT) {
|
|
745
|
+
const myTransferStatus = transferHistory.data.find(
|
|
746
|
+
(operation) =>
|
|
747
|
+
operation.transactionHash?.toLowerCase() ===
|
|
748
|
+
orderId.toString().toLowerCase(),
|
|
749
|
+
);
|
|
750
|
+
if (!myTransferStatus) {
|
|
751
|
+
if (attempt < maxAttempts) {
|
|
752
|
+
logger.info(
|
|
753
|
+
`Deposit operation not found for transactionHash ${orderId}, retrying (attempt ${attempt}/${maxAttempts})...`,
|
|
754
|
+
);
|
|
755
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
logger.warn(
|
|
759
|
+
`Deposit operation not found for transactionHash ${orderId} after ${maxAttempts} attempts`,
|
|
760
|
+
);
|
|
761
|
+
return false;
|
|
762
|
+
}
|
|
763
|
+
// Check if status is COMPLETED
|
|
764
|
+
if (myTransferStatus.status === AssetOperationStatus.COMPLETED) {
|
|
765
|
+
logger.info(`Deposit operation ${orderId} completed successfully`);
|
|
766
|
+
return true;
|
|
767
|
+
} else {
|
|
768
|
+
if (attempt < maxAttempts) {
|
|
769
|
+
logger.info(
|
|
770
|
+
`Deposit operation ${orderId} found but status is ${myTransferStatus.status}, not COMPLETED. Retrying (attempt ${attempt}/${maxAttempts})...`,
|
|
771
|
+
);
|
|
772
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
logger.warn(
|
|
776
|
+
`Deposit operation ${orderId} status is ${myTransferStatus.status} after ${maxAttempts} attempts, expected COMPLETED`,
|
|
777
|
+
);
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
} else {
|
|
781
|
+
const myTransferStatus = transferHistory.data.find(
|
|
782
|
+
(operation) => operation.id === orderId.toString(),
|
|
783
|
+
);
|
|
784
|
+
if (!myTransferStatus) {
|
|
785
|
+
if (attempt < maxAttempts) {
|
|
786
|
+
logger.info(
|
|
787
|
+
`Withdrawal status not found for orderId ${orderId} in completed operations, retrying (attempt ${attempt}/${maxAttempts})...`,
|
|
788
|
+
);
|
|
789
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
logger.warn(
|
|
793
|
+
`Withdrawal operation not found for orderId ${orderId} after ${maxAttempts} attempts`,
|
|
794
|
+
);
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
// Check if status is COMPLETED
|
|
798
|
+
if (myTransferStatus.status === AssetOperationStatus.COMPLETED) {
|
|
799
|
+
logger.info(
|
|
800
|
+
`Withdrawal operation ${orderId} completed successfully`,
|
|
801
|
+
);
|
|
802
|
+
return true;
|
|
803
|
+
} else {
|
|
804
|
+
if (attempt < maxAttempts) {
|
|
805
|
+
logger.info(
|
|
806
|
+
`Withdrawal operation ${orderId} found but status is ${myTransferStatus.status}, not COMPLETED. Retrying (attempt ${attempt}/${maxAttempts})...`,
|
|
807
|
+
);
|
|
808
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
logger.warn(
|
|
812
|
+
`Withdrawal operation ${orderId} status is ${myTransferStatus.status} after ${maxAttempts} attempts, expected COMPLETED`,
|
|
813
|
+
);
|
|
814
|
+
return false;
|
|
815
|
+
}
|
|
647
816
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
817
|
+
} catch (err) {
|
|
818
|
+
logger.error(
|
|
819
|
+
`error getting deposit or withdrawal status (attempt ${attempt}/${maxAttempts}): ${err}`,
|
|
820
|
+
);
|
|
821
|
+
if (attempt < maxAttempts) {
|
|
822
|
+
logger.info(`Retrying after ${retryDelayMs}ms...`);
|
|
823
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
824
|
+
continue;
|
|
653
825
|
}
|
|
654
|
-
|
|
826
|
+
logger.error(
|
|
827
|
+
`Max retry attempts reached for getDepositOrWithdrawalStatus`,
|
|
828
|
+
);
|
|
829
|
+
return false;
|
|
655
830
|
}
|
|
656
|
-
}catch(err){
|
|
657
|
-
logger.error(`error getting deposit or withdrawal status: ${err}`);
|
|
658
|
-
return false;
|
|
659
831
|
}
|
|
832
|
+
|
|
833
|
+
return false;
|
|
660
834
|
}
|
|
661
835
|
}
|