@t2000/sdk 0.18.41 → 0.19.0

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/README.md CHANGED
@@ -5,7 +5,7 @@ The complete TypeScript SDK for AI agent bank accounts on Sui. Send USDC, earn y
5
5
  [![npm](https://img.shields.io/npm/v/@t2000/sdk)](https://www.npmjs.com/package/@t2000/sdk)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- **[Website](https://t2000.ai)** · **[GitHub](https://github.com/mission69b/t2000)** · **[CLI](https://www.npmjs.com/package/@t2000/cli)** · **[x402](https://www.npmjs.com/package/@t2000/x402)** · **[MCP](https://www.npmjs.com/package/@t2000/mcp)**
8
+ **[Website](https://t2000.ai)** · **[GitHub](https://github.com/mission69b/t2000)** · **[CLI](https://www.npmjs.com/package/@t2000/cli)** · **[MPP](https://www.npmjs.com/package/@t2000/mpp-sui)** · **[MCP](https://www.npmjs.com/package/@t2000/mcp)**
9
9
 
10
10
  ## Installation
11
11
 
@@ -319,7 +319,7 @@ formatAssetAmount('SUI', 105.26); // "105.260000000" (9 decimals)
319
319
 
320
320
  ### Advanced: Exposed Internals
321
321
 
322
- For integrations (like `@t2000/x402`), the agent exposes:
322
+ For integrations (like `@t2000/mpp-sui`), the agent exposes:
323
323
 
324
324
  ```typescript
325
325
  agent.suiClient; // SuiJsonRpcClient instance
@@ -437,7 +437,7 @@ SMOKE=1 pnpm --filter @t2000/sdk test -- src/__smoke__
437
437
  | Withdraw | Free | |
438
438
  | Repay | Free | |
439
439
  | Send | Free | |
440
- | Pay (x402) | Free | Agent pays the API price, no t2000 surcharge |
440
+ | Pay (MPP) | Free | Agent pays the API price, no t2000 surcharge |
441
441
 
442
442
  Fees are collected by the t2000 protocol treasury on-chain.
443
443
 
@@ -1,4 +1,4 @@
1
- export { y as AdapterCapability, z as AdapterPositions, J as AdapterTxResult, N as CetusAdapter, Q as HealthInfo, L as LendingAdapter, u as LendingRates, V as NaviAdapter, X as PerpsAdapter, $ as ProtocolDescriptor, a0 as ProtocolRegistry, a3 as SuilendAdapter, d as SwapAdapter, a4 as SwapQuote, a7 as allDescriptors, a8 as cetusDescriptor, ab as naviDescriptor, ae as sentinelDescriptor, ah as suilendDescriptor } from '../index-HADTiik7.cjs';
1
+ export { J as AdapterCapability, K as AdapterPositions, N as AdapterTxResult, Q as CetusAdapter, V as HealthInfo, L as LendingAdapter, w as LendingRates, Y as NaviAdapter, Z as PerpsAdapter, a1 as ProtocolDescriptor, a2 as ProtocolRegistry, a5 as SuilendAdapter, e as SwapAdapter, a6 as SwapQuote, a9 as allDescriptors, aa as cetusDescriptor, ad as naviDescriptor, ag as sentinelDescriptor, aj as suilendDescriptor } from '../index-D-6pQwzx.cjs';
2
2
  import '@mysten/sui/transactions';
3
3
  import '@mysten/sui/jsonRpc';
4
4
  import '@mysten/sui/keypairs/ed25519';
@@ -1,4 +1,4 @@
1
- export { y as AdapterCapability, z as AdapterPositions, J as AdapterTxResult, N as CetusAdapter, Q as HealthInfo, L as LendingAdapter, u as LendingRates, V as NaviAdapter, X as PerpsAdapter, $ as ProtocolDescriptor, a0 as ProtocolRegistry, a3 as SuilendAdapter, d as SwapAdapter, a4 as SwapQuote, a7 as allDescriptors, a8 as cetusDescriptor, ab as naviDescriptor, ae as sentinelDescriptor, ah as suilendDescriptor } from '../index-HADTiik7.js';
1
+ export { J as AdapterCapability, K as AdapterPositions, N as AdapterTxResult, Q as CetusAdapter, V as HealthInfo, L as LendingAdapter, w as LendingRates, Y as NaviAdapter, Z as PerpsAdapter, a1 as ProtocolDescriptor, a2 as ProtocolRegistry, a5 as SuilendAdapter, e as SwapAdapter, a6 as SwapQuote, a9 as allDescriptors, aa as cetusDescriptor, ad as naviDescriptor, ag as sentinelDescriptor, aj as suilendDescriptor } from '../index-D-6pQwzx.js';
2
2
  import '@mysten/sui/transactions';
3
3
  import '@mysten/sui/jsonRpc';
4
4
  import '@mysten/sui/keypairs/ed25519';
@@ -421,6 +421,23 @@ interface TradePositionsResult {
421
421
  totalMargin: number;
422
422
  totalUnrealizedPnL: number;
423
423
  }
424
+ interface PayOptions {
425
+ url: string;
426
+ method?: string;
427
+ body?: string;
428
+ headers?: Record<string, string>;
429
+ maxPrice?: number;
430
+ }
431
+ interface PayResult {
432
+ status: number;
433
+ body: unknown;
434
+ paid: boolean;
435
+ cost?: number;
436
+ receipt?: {
437
+ reference: string;
438
+ timestamp: string;
439
+ };
440
+ }
424
441
 
425
442
  type AdapterCapability = 'save' | 'withdraw' | 'borrow' | 'repay' | 'swap' | 'perps';
426
443
  /**
@@ -760,4 +777,4 @@ declare function attack(client: SuiJsonRpcClient, signer: Ed25519Keypair, sentin
760
777
  /** All registered protocol descriptors — used by the indexer for event classification */
761
778
  declare const allDescriptors: ProtocolDescriptor[];
762
779
 
763
- export { type ProtocolDescriptor as $, type AutoInvestSchedule as A, type BalanceResponse as B, type ClaimRewardsResult as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type AdapterTxResult as J, type AssetRates as K, type LendingAdapter as L, type MaxWithdrawResult as M, CetusAdapter as N, type GasReserve as O, type PendingReward$1 as P, type HealthInfo as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type InvestRebalanceMove as U, NaviAdapter as V, type WithdrawResult as W, type PerpsAdapter as X, type PerpsPosition as Y, type PositionEntry as Z, type PositionSide as _, type AutoInvestStatus as a, ProtocolRegistry as a0, type RebalanceStep as a1, type SentinelVerdict as a2, SuilendAdapter as a3, type SwapQuote as a4, type TradePositionsResult as a5, type TradeResult as a6, allDescriptors as a7, descriptor$2 as a8, getSentinelInfo as a9, listSentinels as aa, descriptor$3 as ab, requestAttack as ac, attack as ad, descriptor as ae, settleAttack as af, submitPrompt as ag, descriptor$1 as ah, type SendResult as b, type TransactionRecord as c, type SwapAdapter as d, type SaveResult as e, type BorrowResult as f, type MaxBorrowResult as g, type SwapResult as h, type InvestResult as i, type InvestEarnResult as j, type InvestRebalanceResult as k, type StrategyBuyResult as l, type StrategySellResult as m, type StrategyRebalanceResult as n, type StrategyStatusResult as o, type AutoInvestRunResult as p, type PortfolioResult as q, type InvestmentPosition as r, type PositionsResult as s, type RatesResult as t, type LendingRates as u, type RebalanceResult as v, type SentinelAgent as w, type SentinelAttackResult as x, type AdapterCapability as y, type AdapterPositions as z };
780
+ export { type PositionEntry as $, type AutoInvestSchedule as A, type BalanceResponse as B, type ClaimRewardsResult as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type AdapterCapability as J, type AdapterPositions as K, type LendingAdapter as L, type MaxWithdrawResult as M, type AdapterTxResult as N, type AssetRates as O, type PayOptions as P, CetusAdapter as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type GasReserve as U, type HealthInfo as V, type WithdrawResult as W, type InvestRebalanceMove as X, NaviAdapter as Y, type PerpsAdapter as Z, type PerpsPosition as _, type AutoInvestStatus as a, type PositionSide as a0, type ProtocolDescriptor as a1, ProtocolRegistry as a2, type RebalanceStep as a3, type SentinelVerdict as a4, SuilendAdapter as a5, type SwapQuote as a6, type TradePositionsResult as a7, type TradeResult as a8, allDescriptors as a9, descriptor$2 as aa, getSentinelInfo as ab, listSentinels as ac, descriptor$3 as ad, requestAttack as ae, attack as af, descriptor as ag, settleAttack as ah, submitPrompt as ai, descriptor$1 as aj, type PayResult as b, type SendResult as c, type TransactionRecord as d, type SwapAdapter as e, type SaveResult as f, type BorrowResult as g, type MaxBorrowResult as h, type SwapResult as i, type InvestResult as j, type InvestEarnResult as k, type InvestRebalanceResult as l, type PendingReward$1 as m, type StrategyBuyResult as n, type StrategySellResult as o, type StrategyRebalanceResult as p, type StrategyStatusResult as q, type AutoInvestRunResult as r, type PortfolioResult as s, type InvestmentPosition as t, type PositionsResult as u, type RatesResult as v, type LendingRates as w, type RebalanceResult as x, type SentinelAgent as y, type SentinelAttackResult as z };
@@ -421,6 +421,23 @@ interface TradePositionsResult {
421
421
  totalMargin: number;
422
422
  totalUnrealizedPnL: number;
423
423
  }
424
+ interface PayOptions {
425
+ url: string;
426
+ method?: string;
427
+ body?: string;
428
+ headers?: Record<string, string>;
429
+ maxPrice?: number;
430
+ }
431
+ interface PayResult {
432
+ status: number;
433
+ body: unknown;
434
+ paid: boolean;
435
+ cost?: number;
436
+ receipt?: {
437
+ reference: string;
438
+ timestamp: string;
439
+ };
440
+ }
424
441
 
425
442
  type AdapterCapability = 'save' | 'withdraw' | 'borrow' | 'repay' | 'swap' | 'perps';
426
443
  /**
@@ -760,4 +777,4 @@ declare function attack(client: SuiJsonRpcClient, signer: Ed25519Keypair, sentin
760
777
  /** All registered protocol descriptors — used by the indexer for event classification */
761
778
  declare const allDescriptors: ProtocolDescriptor[];
762
779
 
763
- export { type ProtocolDescriptor as $, type AutoInvestSchedule as A, type BalanceResponse as B, type ClaimRewardsResult as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type AdapterTxResult as J, type AssetRates as K, type LendingAdapter as L, type MaxWithdrawResult as M, CetusAdapter as N, type GasReserve as O, type PendingReward$1 as P, type HealthInfo as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type InvestRebalanceMove as U, NaviAdapter as V, type WithdrawResult as W, type PerpsAdapter as X, type PerpsPosition as Y, type PositionEntry as Z, type PositionSide as _, type AutoInvestStatus as a, ProtocolRegistry as a0, type RebalanceStep as a1, type SentinelVerdict as a2, SuilendAdapter as a3, type SwapQuote as a4, type TradePositionsResult as a5, type TradeResult as a6, allDescriptors as a7, descriptor$2 as a8, getSentinelInfo as a9, listSentinels as aa, descriptor$3 as ab, requestAttack as ac, attack as ad, descriptor as ae, settleAttack as af, submitPrompt as ag, descriptor$1 as ah, type SendResult as b, type TransactionRecord as c, type SwapAdapter as d, type SaveResult as e, type BorrowResult as f, type MaxBorrowResult as g, type SwapResult as h, type InvestResult as i, type InvestEarnResult as j, type InvestRebalanceResult as k, type StrategyBuyResult as l, type StrategySellResult as m, type StrategyRebalanceResult as n, type StrategyStatusResult as o, type AutoInvestRunResult as p, type PortfolioResult as q, type InvestmentPosition as r, type PositionsResult as s, type RatesResult as t, type LendingRates as u, type RebalanceResult as v, type SentinelAgent as w, type SentinelAttackResult as x, type AdapterCapability as y, type AdapterPositions as z };
780
+ export { type PositionEntry as $, type AutoInvestSchedule as A, type BalanceResponse as B, type ClaimRewardsResult as C, type DepositInfo as D, type EarningsResult as E, type FundStatusResult as F, type GasMethod as G, type HealthFactorResult as H, type InvestmentTrade as I, type AdapterCapability as J, type AdapterPositions as K, type LendingAdapter as L, type MaxWithdrawResult as M, type AdapterTxResult as N, type AssetRates as O, type PayOptions as P, CetusAdapter as Q, type RepayResult as R, type StrategyDefinition as S, type T2000Options as T, type GasReserve as U, type HealthInfo as V, type WithdrawResult as W, type InvestRebalanceMove as X, NaviAdapter as Y, type PerpsAdapter as Z, type PerpsPosition as _, type AutoInvestStatus as a, type PositionSide as a0, type ProtocolDescriptor as a1, ProtocolRegistry as a2, type RebalanceStep as a3, type SentinelVerdict as a4, SuilendAdapter as a5, type SwapQuote as a6, type TradePositionsResult as a7, type TradeResult as a8, allDescriptors as a9, descriptor$2 as aa, getSentinelInfo as ab, listSentinels as ac, descriptor$3 as ad, requestAttack as ae, attack as af, descriptor as ag, settleAttack as ah, submitPrompt as ai, descriptor$1 as aj, type PayResult as b, type SendResult as c, type TransactionRecord as d, type SwapAdapter as e, type SaveResult as f, type BorrowResult as g, type MaxBorrowResult as h, type SwapResult as i, type InvestResult as j, type InvestEarnResult as k, type InvestRebalanceResult as l, type PendingReward$1 as m, type StrategyBuyResult as n, type StrategySellResult as o, type StrategyRebalanceResult as p, type StrategyStatusResult as q, type AutoInvestRunResult as r, type PortfolioResult as s, type InvestmentPosition as t, type PositionsResult as u, type RatesResult as v, type LendingRates as w, type RebalanceResult as x, type SentinelAgent as y, type SentinelAttackResult as z };
package/dist/index.cjs CHANGED
@@ -56766,6 +56766,19 @@ var PortfolioManager = class {
56766
56766
  if (!bucket) return false;
56767
56767
  return Object.values(bucket).some((p) => p.totalAmount > 0);
56768
56768
  }
56769
+ closeStrategyPosition(strategyKey, asset) {
56770
+ this.load();
56771
+ const bucket = this.data.strategies[strategyKey];
56772
+ if (!bucket?.[asset]) return;
56773
+ bucket[asset].totalAmount = 0;
56774
+ bucket[asset].costBasis = 0;
56775
+ bucket[asset].avgPrice = 0;
56776
+ const hasPositions = Object.values(bucket).some((p) => p.totalAmount > 0);
56777
+ if (!hasPositions) {
56778
+ delete this.data.strategies[strategyKey];
56779
+ }
56780
+ this.save();
56781
+ }
56769
56782
  };
56770
56783
  function emptyData2() {
56771
56784
  return { strategies: {} };
@@ -57091,14 +57104,49 @@ var T2000 = class _T2000 extends eventemitter3.EventEmitter {
57091
57104
  return { agent, address, sponsored };
57092
57105
  }
57093
57106
  // -- Gas --
57094
- /** SuiJsonRpcClient used by this agent — exposed for x402 and other integrations. */
57107
+ /** SuiJsonRpcClient used by this agent — exposed for integrations. */
57095
57108
  get suiClient() {
57096
57109
  return this.client;
57097
57110
  }
57098
- /** Ed25519Keypair used by this agent — exposed for x402 and other integrations. */
57111
+ /** Ed25519Keypair used by this agent — exposed for integrations. */
57099
57112
  get signer() {
57100
57113
  return this.keypair;
57101
57114
  }
57115
+ // -- MPP Payments --
57116
+ async pay(options) {
57117
+ this.enforcer.assertNotLocked();
57118
+ this.enforcer.check({ operation: "pay", amount: options.maxPrice ?? 1 });
57119
+ const { Mppx } = await import('mppx/client');
57120
+ const { sui } = await import('@t2000/mpp-sui/client');
57121
+ const mppx = Mppx.create({
57122
+ polyfill: false,
57123
+ methods: [sui({ client: this.client, signer: this.keypair })]
57124
+ });
57125
+ const response = await mppx.fetch(options.url, {
57126
+ method: options.method,
57127
+ headers: options.headers,
57128
+ body: options.body
57129
+ });
57130
+ const contentType = response.headers.get("content-type") ?? "";
57131
+ let body;
57132
+ try {
57133
+ body = contentType.includes("application/json") ? await response.json() : await response.text();
57134
+ } catch {
57135
+ body = null;
57136
+ }
57137
+ const receiptHeader = response.headers.get("x-payment-receipt");
57138
+ const paid = !!receiptHeader;
57139
+ if (paid) {
57140
+ this.enforcer.recordUsage(options.maxPrice ?? 1);
57141
+ }
57142
+ return {
57143
+ status: response.status,
57144
+ body,
57145
+ paid,
57146
+ cost: paid ? options.maxPrice ?? void 0 : void 0,
57147
+ receipt: receiptHeader ? { reference: receiptHeader, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : void 0
57148
+ };
57149
+ }
57102
57150
  // -- Wallet --
57103
57151
  address() {
57104
57152
  return this._address;
@@ -58116,16 +58164,14 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
58116
58164
  if (!(params.asset in INVESTMENT_ASSETS)) {
58117
58165
  throw new T2000Error("ASSET_NOT_SUPPORTED", `${params.asset} is not available for investment`);
58118
58166
  }
58119
- const pos = this.portfolio.getPosition(params.asset);
58120
- if (!pos || pos.totalAmount <= 0) {
58121
- throw new T2000Error("INSUFFICIENT_INVESTMENT", `No ${params.asset} position to sell`);
58122
- }
58123
- const didAutoWithdraw = !!(pos.earning && pos.earningProtocol);
58167
+ let pos = this.portfolio.getPosition(params.asset);
58168
+ const didAutoWithdraw = !!(pos?.earning && pos.earningProtocol);
58124
58169
  if (didAutoWithdraw) {
58125
58170
  const unearnResult = await this.investUnearn({ asset: params.asset });
58126
58171
  if (unearnResult.tx) {
58127
58172
  await this.client.waitForTransaction({ digest: unearnResult.tx, options: { showEffects: true } });
58128
58173
  }
58174
+ pos = this.portfolio.getPosition(params.asset);
58129
58175
  }
58130
58176
  const assetInfo = SUPPORTED_ASSETS[params.asset];
58131
58177
  const gasReserve = params.asset === "SUI" ? GAS_RESERVE_MIN : 0;
@@ -58140,16 +58186,20 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
58140
58186
  await new Promise((r) => setTimeout(r, 1500));
58141
58187
  }
58142
58188
  const maxSellable = Math.max(0, walletAmount - gasReserve);
58189
+ const trackedAmount = pos && pos.totalAmount > 0 ? pos.totalAmount : maxSellable;
58190
+ if (trackedAmount <= 0) {
58191
+ throw new T2000Error("INSUFFICIENT_INVESTMENT", `No ${params.asset} position to sell`);
58192
+ }
58143
58193
  let sellAmountAsset;
58144
58194
  if (params.usdAmount === "all") {
58145
- sellAmountAsset = Math.min(pos.totalAmount, maxSellable);
58195
+ sellAmountAsset = Math.min(trackedAmount, maxSellable);
58146
58196
  } else {
58147
58197
  const swapAdapter = this.registry.listSwap()[0];
58148
58198
  if (!swapAdapter) throw new T2000Error("PROTOCOL_UNAVAILABLE", "No swap adapter available");
58149
58199
  const quote = await swapAdapter.getQuote("USDC", params.asset, 1);
58150
58200
  const assetPrice = 1 / quote.expectedOutput;
58151
58201
  sellAmountAsset = params.usdAmount / assetPrice;
58152
- const maxPosition = params._strategyOnly ? maxSellable : pos.totalAmount;
58202
+ const maxPosition = params._strategyOnly ? maxSellable : trackedAmount;
58153
58203
  sellAmountAsset = Math.min(sellAmountAsset, maxPosition);
58154
58204
  if (sellAmountAsset > maxSellable) {
58155
58205
  throw new T2000Error(
@@ -58184,17 +58234,21 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
58184
58234
  }
58185
58235
  }
58186
58236
  const price = swapResult.toAmount / sellAmountAsset;
58187
- const realizedPnL = this.portfolio.recordSell({
58188
- id: `inv_${Date.now()}`,
58189
- type: "sell",
58190
- asset: params.asset,
58191
- amount: sellAmountAsset,
58192
- price,
58193
- usdValue: swapResult.toAmount,
58194
- fee: swapResult.fee,
58195
- tx: swapResult.tx,
58196
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
58197
- });
58237
+ let realizedPnL = 0;
58238
+ try {
58239
+ realizedPnL = this.portfolio.recordSell({
58240
+ id: `inv_${Date.now()}`,
58241
+ type: "sell",
58242
+ asset: params.asset,
58243
+ amount: sellAmountAsset,
58244
+ price,
58245
+ usdValue: swapResult.toAmount,
58246
+ fee: swapResult.fee,
58247
+ tx: swapResult.tx,
58248
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
58249
+ });
58250
+ } catch {
58251
+ }
58198
58252
  if (params.usdAmount === "all" && !params._strategyOnly) {
58199
58253
  this.portfolio.closePosition(params.asset);
58200
58254
  }
@@ -58697,34 +58751,47 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
58697
58751
  for (const meta of swapMetas) {
58698
58752
  const usdValue = meta.estimatedOut / 10 ** meta.toDecimals;
58699
58753
  const price = meta.amount > 0 ? usdValue / meta.amount : 0;
58700
- const pnl = this.portfolio.recordStrategySell(params.strategy, {
58701
- id: `strat_sell_${Date.now()}_${meta.asset}`,
58702
- type: "sell",
58703
- asset: meta.asset,
58704
- amount: meta.amount,
58705
- price,
58706
- usdValue,
58707
- fee: 0,
58708
- tx: digest,
58709
- timestamp: now
58710
- });
58711
- this.portfolio.recordSell({
58712
- id: `inv_sell_${Date.now()}_${meta.asset}`,
58713
- type: "sell",
58714
- asset: meta.asset,
58715
- amount: meta.amount,
58716
- price,
58717
- usdValue,
58718
- fee: 0,
58719
- tx: digest,
58720
- timestamp: now
58721
- });
58754
+ let pnl = 0;
58755
+ try {
58756
+ pnl = this.portfolio.recordStrategySell(params.strategy, {
58757
+ id: `strat_sell_${Date.now()}_${meta.asset}`,
58758
+ type: "sell",
58759
+ asset: meta.asset,
58760
+ amount: meta.amount,
58761
+ price,
58762
+ usdValue,
58763
+ fee: 0,
58764
+ tx: digest,
58765
+ timestamp: now
58766
+ });
58767
+ } catch {
58768
+ }
58769
+ try {
58770
+ this.portfolio.recordSell({
58771
+ id: `inv_sell_${Date.now()}_${meta.asset}`,
58772
+ type: "sell",
58773
+ asset: meta.asset,
58774
+ amount: meta.amount,
58775
+ price,
58776
+ usdValue,
58777
+ fee: 0,
58778
+ tx: digest,
58779
+ timestamp: now
58780
+ });
58781
+ } catch {
58782
+ }
58722
58783
  sells.push({ asset: meta.asset, amount: meta.amount, usdValue, realizedPnL: pnl, tx: digest });
58723
58784
  totalProceeds += usdValue;
58724
58785
  totalPnL += pnl;
58725
58786
  }
58726
- if (unearnFailures.length === 0 && this.portfolio.hasStrategyPositions(params.strategy)) {
58727
- this.portfolio.clearStrategy(params.strategy);
58787
+ if (this.portfolio.hasStrategyPositions(params.strategy)) {
58788
+ if (unearnFailures.length === 0) {
58789
+ this.portfolio.clearStrategy(params.strategy);
58790
+ } else {
58791
+ for (const s of sells) {
58792
+ this.portfolio.closeStrategyPosition(params.strategy, s.asset);
58793
+ }
58794
+ }
58728
58795
  }
58729
58796
  const failed = unearnFailures.map((f) => ({ asset: f.asset, reason: f.error }));
58730
58797
  return {