@haven-fi/solauto-sdk 1.0.830 → 1.0.833

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.
@@ -11,6 +11,8 @@ export declare class MarginfiFlProvider extends FlProviderBase {
11
11
  private debtImfiAccount;
12
12
  private supplyRemainingAccounts;
13
13
  private debtRemainingAccounts;
14
+ private supplyBankHealthAccounts;
15
+ private debtBankHealthAccounts;
14
16
  initialize(): Promise<void>;
15
17
  private setAvailableBanks;
16
18
  private setIntermediaryAccount;
@@ -19,6 +21,7 @@ export declare class MarginfiFlProvider extends FlProviderBase {
19
21
  liquiditySource(source: TokenType): PublicKey;
20
22
  private liquidityBank;
21
23
  private iMfiAccount;
24
+ private bankHealthAccounts;
22
25
  liquidityAvailable(source: TokenType): bigint;
23
26
  flFeeBps(source: TokenType, signerFlashLoan?: boolean): number;
24
27
  flashBorrow(flashLoan: FlashLoanDetails, destTokenAccount: PublicKey): TransactionBuilder;
@@ -1 +1 @@
1
- {"version":3,"file":"marginfiFlProvider.d.ts","sourceRoot":"","sources":["../../../src/services/flashLoans/marginfiFlProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA8B,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAML,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAclC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAkBlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAQ5C,qBAAa,kBAAmB,SAAQ,cAAc;IACpD,OAAO,CAAC,wBAAwB,CAAqB;IACrD,OAAO,CAAC,yBAAyB,CAAQ;IACzC,OAAO,CAAC,uBAAuB,CAAQ;IAEvC,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,eAAe,CAAe;IACtC,OAAO,CAAC,uBAAuB,CAAiB;IAChD,OAAO,CAAC,qBAAqB,CAAiB;IAExC,UAAU;YAiBF,iBAAiB;YA6CjB,sBAAsB;IAsD9B,sBAAsB,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAyC3D,gBAAgB,IAAI,SAAS,EAAE;IAY/B,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS;IAI7C,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,WAAW;IAMnB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM;IAI7C,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM;IAc9D,WAAW,CACT,SAAS,EAAE,gBAAgB,EAC3B,gBAAgB,EAAE,SAAS,GAC1B,kBAAkB;IAoCrB,UAAU,CAAC,SAAS,EAAE,gBAAgB,GAAG,kBAAkB;IAiD3D,YAAY,CACV,eAAe,EAAE,SAAS,EAC1B,IAAI,EAAE,SAAS,EACf,aAAa,EAAE,SAAS;CAW3B"}
1
+ {"version":3,"file":"marginfiFlProvider.d.ts","sourceRoot":"","sources":["../../../src/services/flashLoans/marginfiFlProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA8B,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAML,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAclC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAmBlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAQ5C,qBAAa,kBAAmB,SAAQ,cAAc;IACpD,OAAO,CAAC,wBAAwB,CAAqB;IACrD,OAAO,CAAC,yBAAyB,CAAQ;IACzC,OAAO,CAAC,uBAAuB,CAAQ;IAEvC,OAAO,CAAC,iBAAiB,CAAe;IACxC,OAAO,CAAC,eAAe,CAAe;IACtC,OAAO,CAAC,uBAAuB,CAAiB;IAChD,OAAO,CAAC,qBAAqB,CAAiB;IAC9C,OAAO,CAAC,wBAAwB,CAAiB;IACjD,OAAO,CAAC,sBAAsB,CAAiB;IAEzC,UAAU;YAiBF,iBAAiB;YAgEjB,sBAAsB;IAsD9B,sBAAsB,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAyC3D,gBAAgB,IAAI,SAAS,EAAE;IAY/B,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS;IAI7C,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,kBAAkB;IAM1B,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM;IAI7C,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,eAAe,CAAC,EAAE,OAAO,GAAG,MAAM;IAc9D,WAAW,CACT,SAAS,EAAE,gBAAgB,EAC3B,gBAAgB,EAAE,SAAS,GAC1B,kBAAkB;IAsDrB,UAAU,CAAC,SAAS,EAAE,gBAAgB,GAAG,kBAAkB;IAiD3D,YAAY,CACV,eAAe,EAAE,SAAS,EAC1B,IAAI,EAAE,SAAS,EACf,aAAa,EAAE,SAAS;CAW3B"}
@@ -51,6 +51,22 @@ class MarginfiFlProvider extends flProviderBase_1.FlProviderBase {
51
51
  const debtBanks = mapBanksAndBalances(this.debtMint);
52
52
  this.supplyBankLiquiditySource = supplyBanks[0][1];
53
53
  this.debtBankLiquiditySource = debtBanks[0][1];
54
+ const [supplyPriceOracle, debtPriceOracle] = await Promise.all([
55
+ (0, utils_1.getMarginfiPriceOracle)(this.umi, {
56
+ data: this.supplyBankLiquiditySource,
57
+ }),
58
+ (0, utils_1.getMarginfiPriceOracle)(this.umi, {
59
+ data: this.debtBankLiquiditySource,
60
+ }),
61
+ ]);
62
+ this.supplyBankHealthAccounts = [
63
+ (0, utils_1.getAccountMeta)((0, umi_web3js_adapters_1.toWeb3JsPublicKey)(this.supplyBankLiquiditySource.publicKey)),
64
+ (0, utils_1.getAccountMeta)(supplyPriceOracle),
65
+ ];
66
+ this.debtBankHealthAccounts = [
67
+ (0, utils_1.getAccountMeta)((0, umi_web3js_adapters_1.toWeb3JsPublicKey)(this.debtBankLiquiditySource.publicKey)),
68
+ (0, utils_1.getAccountMeta)(debtPriceOracle),
69
+ ];
54
70
  }
55
71
  async setIntermediaryAccount(sources) {
56
72
  const compatibleMarginfiAccounts = this.existingMarginfiAccounts.filter((x) => x.group.toString() == this.liquidityBank(sources[0]).group);
@@ -137,6 +153,11 @@ class MarginfiFlProvider extends flProviderBase_1.FlProviderBase {
137
153
  ? this.supplyImfiAccount
138
154
  : this.debtImfiAccount;
139
155
  }
156
+ bankHealthAccounts(source) {
157
+ return source === generated_1.TokenType.Supply
158
+ ? this.supplyBankHealthAccounts
159
+ : this.debtBankHealthAccounts;
160
+ }
140
161
  liquidityAvailable(source) {
141
162
  return (0, utils_1.getBankLiquidityAvailableBaseUnit)(this.liquidityBank(source), false);
142
163
  }
@@ -154,6 +175,15 @@ class MarginfiFlProvider extends flProviderBase_1.FlProviderBase {
154
175
  const bank = this.liquidityBank(flashLoan.liquiditySource);
155
176
  const associatedBankAccs = (0, utils_1.findMarginfiAccounts)((0, umi_web3js_adapters_1.toWeb3JsPublicKey)(bank.publicKey));
156
177
  const iMfiAccount = this.iMfiAccount(flashLoan.liquiditySource);
178
+ const existingRemainingAccounts = flashLoan.liquiditySource === generated_1.TokenType.Supply
179
+ ? this.supplyRemainingAccounts
180
+ : this.debtRemainingAccounts;
181
+ // Marginfi's circuit-breaker gate scans every active bank during borrow,
182
+ // including borrows made while the account is inside a flash loan.
183
+ const borrowRemainingAccounts = [...existingRemainingAccounts];
184
+ if (!borrowRemainingAccounts.some((account) => account.pubkey.toString() === bank.publicKey.toString())) {
185
+ borrowRemainingAccounts.push(...this.bankHealthAccounts(flashLoan.liquiditySource));
186
+ }
157
187
  return (0, umi_1.transactionBuilder)()
158
188
  .add((0, marginfi_1.lendingAccountStartFlashloan)(this.umi, {
159
189
  endIndex: 0, // We set this after building the transaction
@@ -170,7 +200,7 @@ class MarginfiFlProvider extends flProviderBase_1.FlProviderBase {
170
200
  marginfiAccount: (0, umi_1.publicKey)(iMfiAccount.accountPk),
171
201
  marginfiGroup: this.liquidityBank(flashLoan.liquiditySource).group,
172
202
  signer: this.signer,
173
- }));
203
+ }).addRemainingAccounts((0, utils_1.composeRemainingAccounts)(borrowRemainingAccounts)));
174
204
  }
175
205
  flashRepay(flashLoan) {
176
206
  if (flashLoan.signerFlashLoan) {
@@ -60,6 +60,7 @@ export declare class TransactionsManager<T extends TxHandler> {
60
60
  private shouldProceedToSend;
61
61
  private refreshItemSets;
62
62
  private processTransactionsAtomically;
63
+ private processTransactionSetsSequentially;
63
64
  private sendJitoBundle;
64
65
  private processTransactionSet;
65
66
  protected sendTransaction(tx: TransactionBuilder, name: string, attemptNum: number, priorityFeeSetting?: PriorityFeeSetting, txRunType?: TransactionRunType): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"transactionsManager.d.ts","sourceRoot":"","sources":["../../../../src/services/transactions/manager/transactionsManager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"transactionsManager.d.ts","sourceRoot":"","sources":["../../../../src/services/transactions/manager/transactionsManager.ts"],"names":[],"mappings":"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"}
@@ -161,11 +161,7 @@ class TransactionsManager {
161
161
  await this.processTransactionsAtomically(itemSets);
162
162
  }
163
163
  else {
164
- let currentIndex = 0;
165
- while (currentIndex < itemSets.length) {
166
- await this.processTransactionSet(itemSets, currentIndex);
167
- currentIndex++;
168
- }
164
+ await this.processTransactionSetsSequentially(itemSets);
169
165
  }
170
166
  return this.statuses;
171
167
  }
@@ -215,27 +211,49 @@ class TransactionsManager {
215
211
  return newItemSets;
216
212
  }
217
213
  async processTransactionsAtomically(itemSets) {
218
- await (0, utils_1.retryWithExponentialBackoff)(async (attemptNum, prevError) => {
219
- if (prevError &&
220
- this.statuses.filter((x) => x.simulationSuccessful).length >
221
- this.signableRetries) {
222
- throw prevError;
223
- }
224
- this.priorityFeeSetting = this.getUpdatedPriorityFeeSetting(prevError, attemptNum);
225
- if (attemptNum > 0) {
226
- const refreshedSets = await this.refreshItemSets(itemSets, attemptNum, prevError);
227
- if (!refreshedSets || !refreshedSets.length) {
228
- return;
214
+ let bundleSendAttempted = false;
215
+ try {
216
+ await (0, utils_1.retryWithExponentialBackoff)(async (attemptNum, prevError) => {
217
+ if (prevError &&
218
+ this.statuses.filter((x) => x.simulationSuccessful).length >
219
+ this.signableRetries) {
220
+ throw prevError;
221
+ }
222
+ this.priorityFeeSetting = this.getUpdatedPriorityFeeSetting(prevError, attemptNum);
223
+ if (attemptNum > 0) {
224
+ const refreshedSets = await this.refreshItemSets(itemSets, attemptNum, prevError);
225
+ if (!refreshedSets || !refreshedSets.length) {
226
+ return;
227
+ }
228
+ else {
229
+ itemSets = refreshedSets;
230
+ }
229
231
  }
230
- else {
231
- itemSets = refreshedSets;
232
+ if (!this.shouldProceedToSend(itemSets, attemptNum)) {
233
+ return;
232
234
  }
235
+ bundleSendAttempted = true;
236
+ await this.sendJitoBundle(itemSets, attemptNum);
237
+ }, this.totalRetries, this.retryDelay, this.errorsToThrow);
238
+ }
239
+ catch (error) {
240
+ const explicitlyNonRetryable = this.errorsToThrow?.some((errorType) => error instanceof errorType);
241
+ if (!bundleSendAttempted ||
242
+ itemSets.length <= 1 ||
243
+ explicitlyNonRetryable ||
244
+ this.abortController?.signal.aborted) {
245
+ throw error;
233
246
  }
234
- if (!this.shouldProceedToSend(itemSets, attemptNum)) {
235
- return;
236
- }
237
- await this.sendJitoBundle(itemSets, attemptNum);
238
- }, this.totalRetries, this.retryDelay, this.errorsToThrow);
247
+ (0, utils_1.consoleLog)("Jito bundle failed after all retries; retrying one transaction at a time");
248
+ await this.processTransactionSetsSequentially(itemSets);
249
+ }
250
+ }
251
+ async processTransactionSetsSequentially(itemSets) {
252
+ let currentIndex = 0;
253
+ while (currentIndex < itemSets.length) {
254
+ await this.processTransactionSet(itemSets, currentIndex);
255
+ currentIndex++;
256
+ }
239
257
  }
240
258
  async sendJitoBundle(itemSets, attemptNum) {
241
259
  let transactions = [];
@@ -1 +1 @@
1
- {"version":3,"file":"general.d.ts","sourceRoot":"","sources":["../../../src/utils/marginfi/general.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAa,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAKhF,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAEL,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,OAAO,EACP,IAAI,EAIJ,eAAe,EAGhB,MAAM,6BAA6B,CAAC;AASrC,wBAAgB,4BAA4B,CAAC,GAAG,CAAC,EAAE,UAAU,GAAG,OAAO,CActE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,UAAU,OASxE;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,GAAG,EACR,eAAe,EAAE,uBAAuB,GACvC,OAAO,CAAC,SAAS,EAAE,CAAC,CA8BtB;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS;;;;;GAanE;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,GAAG,EACR,IAAI,EAAE;IAAE,EAAE,CAAC,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,CAAA;CAAE,sBAiBtC;AAED,UAAU,wBAAyB,SAAQ,qBAAqB;IAC9D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,GACd,wBAAwB,CAyB1B;AAED,wBAAsB,0CAA0C,CAC9D,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,WAAW,EAAE,CAAC,CAWxB;AAED,wBAAgB,oCAAoC,CAClD,UAAU,EAAE,IAAI,EAChB,QAAQ,EAAE,IAAI,EACd,WAAW,EAAE,MAAM,GAClB,CAAC,MAAM,EAAE,MAAM,CAAC,CA6BlB;AAED,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,eAAe,WASpE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAW3E"}
1
+ {"version":3,"file":"general.d.ts","sourceRoot":"","sources":["../../../src/utils/marginfi/general.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAa,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAKhF,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAEL,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,OAAO,EACP,IAAI,EAIJ,eAAe,EAGhB,MAAM,6BAA6B,CAAC;AASrC,wBAAgB,4BAA4B,CAAC,GAAG,CAAC,EAAE,UAAU,GAAG,OAAO,CActE;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,UAAU,OASxE;AAED,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,GAAG,EACR,eAAe,EAAE,uBAAuB,GACvC,OAAO,CAAC,SAAS,EAAE,CAAC,CA8BtB;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS;;;;;GAanE;AAED,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,GAAG,EACR,IAAI,EAAE;IAAE,EAAE,CAAC,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,CAAA;CAAE,sBAiBtC;AAED,UAAU,wBAAyB,SAAQ,qBAAqB;IAC9D,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,SAAS,GACd,wBAAwB,CAyB1B;AAED,wBAAsB,0CAA0C,CAC9D,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,WAAW,EAAE,CAAC,CAWxB;AAED,wBAAgB,oCAAoC,CAClD,UAAU,EAAE,IAAI,EAChB,QAAQ,EAAE,IAAI,EACd,WAAW,EAAE,MAAM,GAClB,CAAC,MAAM,EAAE,MAAM,CAAC,CA6BlB;AAED,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,eAAe,WASpE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAoB3E"}
@@ -141,6 +141,15 @@ function marginfiAccountEmpty(marginfiAccount) {
141
141
  function composeRemainingAccounts(accs) {
142
142
  const banksAndOracles = accs.reduce((acc, _, i) => i % 2 === 0 ? [...acc, [accs[i], accs[i + 1]]] : acc, []);
143
143
  return banksAndOracles
144
- .sort((a, b) => b[0].pubkey.toString().localeCompare(a[0].pubkey.toString()))
144
+ .sort((a, b) => {
145
+ const aBytes = (0, umi_web3js_adapters_1.toWeb3JsPublicKey)(a[0].pubkey).toBuffer();
146
+ const bBytes = (0, umi_web3js_adapters_1.toWeb3JsPublicKey)(b[0].pubkey).toBuffer();
147
+ for (let i = 0; i < aBytes.length; i++) {
148
+ if (aBytes[i] !== bBytes[i]) {
149
+ return bBytes[i] - aBytes[i];
150
+ }
151
+ }
152
+ return 0;
153
+ })
145
154
  .flat();
146
155
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haven-fi/solauto-sdk",
3
- "version": "1.0.830",
3
+ "version": "1.0.833",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "description": "Typescript SDK for the Solauto program on the Solana blockchain",
@@ -30,6 +30,7 @@ import {
30
30
  fromBaseUnit,
31
31
  getBankLiquidityAvailableBaseUnit,
32
32
  getEmptyMarginfiAccountsByAuthority,
33
+ getAccountMeta,
33
34
  getMarginfiPriceOracle,
34
35
  getRemainingAccountsForMarginfiHealthCheck,
35
36
  getTokenAccount,
@@ -56,6 +57,8 @@ export class MarginfiFlProvider extends FlProviderBase {
56
57
  private debtImfiAccount!: IMFIAccount;
57
58
  private supplyRemainingAccounts!: AccountMeta[];
58
59
  private debtRemainingAccounts!: AccountMeta[];
60
+ private supplyBankHealthAccounts!: AccountMeta[];
61
+ private debtBankHealthAccounts!: AccountMeta[];
59
62
 
60
63
  async initialize() {
61
64
  await this.setAvailableBanks();
@@ -117,6 +120,25 @@ export class MarginfiFlProvider extends FlProviderBase {
117
120
 
118
121
  this.supplyBankLiquiditySource = supplyBanks[0][1];
119
122
  this.debtBankLiquiditySource = debtBanks[0][1];
123
+
124
+ const [supplyPriceOracle, debtPriceOracle] = await Promise.all([
125
+ getMarginfiPriceOracle(this.umi, {
126
+ data: this.supplyBankLiquiditySource,
127
+ }),
128
+ getMarginfiPriceOracle(this.umi, {
129
+ data: this.debtBankLiquiditySource,
130
+ }),
131
+ ]);
132
+ this.supplyBankHealthAccounts = [
133
+ getAccountMeta(
134
+ toWeb3JsPublicKey(this.supplyBankLiquiditySource.publicKey)
135
+ ),
136
+ getAccountMeta(supplyPriceOracle),
137
+ ];
138
+ this.debtBankHealthAccounts = [
139
+ getAccountMeta(toWeb3JsPublicKey(this.debtBankLiquiditySource.publicKey)),
140
+ getAccountMeta(debtPriceOracle),
141
+ ];
120
142
  }
121
143
 
122
144
  private async setIntermediaryAccount(sources: TokenType[]) {
@@ -242,6 +264,12 @@ export class MarginfiFlProvider extends FlProviderBase {
242
264
  : this.debtImfiAccount;
243
265
  }
244
266
 
267
+ private bankHealthAccounts(source: TokenType): AccountMeta[] {
268
+ return source === TokenType.Supply
269
+ ? this.supplyBankHealthAccounts
270
+ : this.debtBankHealthAccounts;
271
+ }
272
+
245
273
  liquidityAvailable(source: TokenType): bigint {
246
274
  return getBankLiquidityAvailableBaseUnit(this.liquidityBank(source), false);
247
275
  }
@@ -273,6 +301,22 @@ export class MarginfiFlProvider extends FlProviderBase {
273
301
  toWeb3JsPublicKey(bank.publicKey)
274
302
  );
275
303
  const iMfiAccount = this.iMfiAccount(flashLoan.liquiditySource)!;
304
+ const existingRemainingAccounts =
305
+ flashLoan.liquiditySource === TokenType.Supply
306
+ ? this.supplyRemainingAccounts
307
+ : this.debtRemainingAccounts;
308
+ // Marginfi's circuit-breaker gate scans every active bank during borrow,
309
+ // including borrows made while the account is inside a flash loan.
310
+ const borrowRemainingAccounts = [...existingRemainingAccounts];
311
+ if (
312
+ !borrowRemainingAccounts.some(
313
+ (account) => account.pubkey.toString() === bank.publicKey.toString()
314
+ )
315
+ ) {
316
+ borrowRemainingAccounts.push(
317
+ ...this.bankHealthAccounts(flashLoan.liquiditySource)
318
+ );
319
+ }
276
320
 
277
321
  return transactionBuilder()
278
322
  .add(
@@ -295,7 +339,9 @@ export class MarginfiFlProvider extends FlProviderBase {
295
339
  marginfiAccount: publicKey(iMfiAccount.accountPk),
296
340
  marginfiGroup: this.liquidityBank(flashLoan.liquiditySource).group,
297
341
  signer: this.signer,
298
- })
342
+ }).addRemainingAccounts(
343
+ composeRemainingAccounts(borrowRemainingAccounts)
344
+ )
299
345
  );
300
346
  }
301
347
 
@@ -279,11 +279,7 @@ export class TransactionsManager<T extends TxHandler> {
279
279
  if (this.atomically) {
280
280
  await this.processTransactionsAtomically(itemSets);
281
281
  } else {
282
- let currentIndex = 0;
283
- while (currentIndex < itemSets.length) {
284
- await this.processTransactionSet(itemSets, currentIndex);
285
- currentIndex++;
286
- }
282
+ await this.processTransactionSetsSequentially(itemSets);
287
283
  }
288
284
 
289
285
  return this.statuses;
@@ -366,44 +362,75 @@ export class TransactionsManager<T extends TxHandler> {
366
362
  }
367
363
 
368
364
  private async processTransactionsAtomically(itemSets: TransactionSet[]) {
369
- await retryWithExponentialBackoff(
370
- async (attemptNum, prevError) => {
371
- if (
372
- prevError &&
373
- this.statuses.filter((x) => x.simulationSuccessful).length >
374
- this.signableRetries
375
- ) {
376
- throw prevError;
377
- }
378
-
379
- this.priorityFeeSetting = this.getUpdatedPriorityFeeSetting(
380
- prevError,
381
- attemptNum
382
- );
365
+ let bundleSendAttempted = false;
366
+ try {
367
+ await retryWithExponentialBackoff(
368
+ async (attemptNum, prevError) => {
369
+ if (
370
+ prevError &&
371
+ this.statuses.filter((x) => x.simulationSuccessful).length >
372
+ this.signableRetries
373
+ ) {
374
+ throw prevError;
375
+ }
383
376
 
384
- if (attemptNum > 0) {
385
- const refreshedSets = await this.refreshItemSets(
386
- itemSets,
387
- attemptNum,
388
- prevError
377
+ this.priorityFeeSetting = this.getUpdatedPriorityFeeSetting(
378
+ prevError,
379
+ attemptNum
389
380
  );
390
- if (!refreshedSets || !refreshedSets.length) {
381
+
382
+ if (attemptNum > 0) {
383
+ const refreshedSets = await this.refreshItemSets(
384
+ itemSets,
385
+ attemptNum,
386
+ prevError
387
+ );
388
+ if (!refreshedSets || !refreshedSets.length) {
389
+ return;
390
+ } else {
391
+ itemSets = refreshedSets;
392
+ }
393
+ }
394
+
395
+ if (!this.shouldProceedToSend(itemSets, attemptNum)) {
391
396
  return;
392
- } else {
393
- itemSets = refreshedSets;
394
397
  }
395
- }
396
398
 
397
- if (!this.shouldProceedToSend(itemSets, attemptNum)) {
398
- return;
399
- }
399
+ bundleSendAttempted = true;
400
+ await this.sendJitoBundle(itemSets, attemptNum);
401
+ },
402
+ this.totalRetries,
403
+ this.retryDelay,
404
+ this.errorsToThrow
405
+ );
406
+ } catch (error: any) {
407
+ const explicitlyNonRetryable = this.errorsToThrow?.some(
408
+ (errorType) => error instanceof errorType
409
+ );
410
+ if (
411
+ !bundleSendAttempted ||
412
+ itemSets.length <= 1 ||
413
+ explicitlyNonRetryable ||
414
+ this.abortController?.signal.aborted
415
+ ) {
416
+ throw error;
417
+ }
400
418
 
401
- await this.sendJitoBundle(itemSets, attemptNum);
402
- },
403
- this.totalRetries,
404
- this.retryDelay,
405
- this.errorsToThrow
406
- );
419
+ consoleLog(
420
+ "Jito bundle failed after all retries; retrying one transaction at a time"
421
+ );
422
+ await this.processTransactionSetsSequentially(itemSets);
423
+ }
424
+ }
425
+
426
+ private async processTransactionSetsSequentially(
427
+ itemSets: TransactionSet[]
428
+ ) {
429
+ let currentIndex = 0;
430
+ while (currentIndex < itemSets.length) {
431
+ await this.processTransactionSet(itemSets, currentIndex);
432
+ currentIndex++;
433
+ }
407
434
  }
408
435
 
409
436
  private async sendJitoBundle(itemSets: TransactionSet[], attemptNum: number) {
@@ -227,8 +227,17 @@ export function composeRemainingAccounts(accs: AccountMeta[]): AccountMeta[] {
227
227
  []
228
228
  );
229
229
  return banksAndOracles
230
- .sort((a, b) =>
231
- b[0].pubkey.toString().localeCompare(a[0].pubkey.toString())
232
- )
230
+ .sort((a, b) => {
231
+ const aBytes = toWeb3JsPublicKey(a[0].pubkey).toBuffer();
232
+ const bBytes = toWeb3JsPublicKey(b[0].pubkey).toBuffer();
233
+
234
+ for (let i = 0; i < aBytes.length; i++) {
235
+ if (aBytes[i] !== bBytes[i]) {
236
+ return bBytes[i] - aBytes[i];
237
+ }
238
+ }
239
+
240
+ return 0;
241
+ })
233
242
  .flat();
234
243
  }
@@ -0,0 +1,45 @@
1
+ import { assert } from "chai";
2
+ import { describe, it } from "mocha";
3
+ import { AccountMeta, publicKey } from "@metaplex-foundation/umi";
4
+ import { composeRemainingAccounts } from "../../src";
5
+
6
+ function readonlyMeta(address: string): AccountMeta {
7
+ return {
8
+ pubkey: publicKey(address),
9
+ isSigner: false,
10
+ isWritable: false,
11
+ };
12
+ }
13
+
14
+ describe("Marginfi remaining accounts", () => {
15
+ it("sorts bank groups by descending public-key bytes", () => {
16
+ // Base58 locale ordering puts DMoq before DeyH, but Marginfi's Rust
17
+ // comparator orders their decoded public-key bytes in the opposite order.
18
+ const deYhBank = readonlyMeta(
19
+ "DeyH7QxWvnbbaVB4zFrf4hoq7Q8z1ZT14co42BGwGtfM"
20
+ );
21
+ const deYhOracle = readonlyMeta(
22
+ "SysvarC1ock11111111111111111111111111111111"
23
+ );
24
+ const dMoqBank = readonlyMeta(
25
+ "DMoqjmsuoru986HgfjqrKEvPv8YBufvBGADHUonkadC5"
26
+ );
27
+ const dMoqOracle = readonlyMeta(
28
+ "SysvarRent111111111111111111111111111111111"
29
+ );
30
+
31
+ const result = composeRemainingAccounts([
32
+ dMoqBank,
33
+ dMoqOracle,
34
+ deYhBank,
35
+ deYhOracle,
36
+ ]);
37
+
38
+ assert.deepEqual(
39
+ result.map((account) => account.pubkey.toString()),
40
+ [deYhBank, deYhOracle, dMoqBank, dMoqOracle].map((account) =>
41
+ account.pubkey.toString()
42
+ )
43
+ );
44
+ });
45
+ });
@@ -0,0 +1,69 @@
1
+ import { expect } from "chai";
2
+ import { describe, it } from "mocha";
3
+ import { TransactionsManager } from "../../src";
4
+
5
+ function createManager(abortController?: AbortController) {
6
+ return new TransactionsManager({
7
+ txHandler: {
8
+ defaultLookupTables: () => [],
9
+ umi: {},
10
+ } as any,
11
+ retryConfig: { totalRetries: 1, retryDelay: 0 },
12
+ abortController,
13
+ }) as any;
14
+ }
15
+
16
+ describe("TransactionsManager bundle fallback", () => {
17
+ it("retries a failed multi-transaction bundle sequentially", async () => {
18
+ const manager = createManager();
19
+ const calls: string[] = [];
20
+ const itemSets = [{}, {}, {}] as any[];
21
+
22
+ manager.shouldProceedToSend = () => true;
23
+ manager.sendJitoBundle = async () => {
24
+ calls.push("bundle");
25
+ throw new Error("bundle failed");
26
+ };
27
+ manager.processTransactionSet = async (
28
+ receivedSets: any[],
29
+ currentIndex: number
30
+ ) => {
31
+ expect(receivedSets).to.equal(itemSets);
32
+ calls.push(`single:${currentIndex}`);
33
+ };
34
+
35
+ await manager.processTransactionsAtomically(itemSets);
36
+
37
+ expect(calls).to.deep.equal([
38
+ "bundle",
39
+ "single:0",
40
+ "single:1",
41
+ "single:2",
42
+ ]);
43
+ });
44
+
45
+ it("does not fall back after the request is aborted", async () => {
46
+ const abortController = new AbortController();
47
+ const manager = createManager(abortController);
48
+ let sequentialAttempts = 0;
49
+
50
+ manager.shouldProceedToSend = () => true;
51
+ manager.sendJitoBundle = async () => {
52
+ abortController.abort();
53
+ throw new Error("bundle aborted");
54
+ };
55
+ manager.processTransactionSet = async () => {
56
+ sequentialAttempts++;
57
+ };
58
+
59
+ let thrownError: Error | undefined;
60
+ try {
61
+ await manager.processTransactionsAtomically([{}, {}]);
62
+ } catch (error: any) {
63
+ thrownError = error;
64
+ }
65
+
66
+ expect(thrownError?.message).to.equal("bundle aborted");
67
+ expect(sequentialAttempts).to.equal(0);
68
+ });
69
+ });