@t2000/sdk 0.14.1 → 0.15.1
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 +10 -2
- package/dist/adapters/index.cjs +48 -35
- package/dist/adapters/index.cjs.map +1 -1
- package/dist/adapters/index.d.cts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +48 -35
- package/dist/adapters/index.js.map +1 -1
- package/dist/{index-B14ZyQZt.d.cts → index-BykavuDO.d.cts} +14 -1
- package/dist/{index-B14ZyQZt.d.ts → index-BykavuDO.d.ts} +14 -1
- package/dist/index.cjs +213 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +213 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { I as InvestmentTrade, T as T2000Options, S as SendResult, B as BalanceResponse, a as TransactionRecord, D as DepositInfo, L as LendingAdapter, b as SwapAdapter, c as SaveResult, W as WithdrawResult, M as MaxWithdrawResult, d as BorrowResult, R as RepayResult, e as MaxBorrowResult, H as HealthFactorResult, f as SwapResult, g as InvestResult, h as InvestEarnResult, P as PortfolioResult, i as PositionsResult, j as RatesResult, k as LendingRates, l as RebalanceResult, E as EarningsResult, F as FundStatusResult, m as SentinelAgent, n as SentinelAttackResult, G as GasMethod } from './index-BykavuDO.cjs';
|
|
2
|
+
export { A as AdapterCapability, o as AdapterPositions, p as AdapterTxResult, q as AssetRates, C as CetusAdapter, r as GasReserve, s as HealthInfo, t as InvestmentPosition, N as NaviAdapter, u as PerpsAdapter, v as PerpsPosition, w as PositionEntry, x as PositionSide, y as ProtocolDescriptor, z as ProtocolRegistry, J as RebalanceStep, K as SentinelVerdict, O as SuilendAdapter, Q as SwapQuote, U as TradePositionsResult, V as TradeResult, X as allDescriptors, Y as cetusDescriptor, Z as getSentinelInfo, _ as listSentinels, $ as naviDescriptor, a0 as requestAttack, a1 as sentinelAttack, a2 as sentinelDescriptor, a3 as settleAttack, a4 as submitPrompt, a5 as suilendDescriptor } from './index-BykavuDO.cjs';
|
|
1
3
|
import { EventEmitter } from 'eventemitter3';
|
|
2
4
|
import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
|
|
3
5
|
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
4
|
-
import { I as InvestmentTrade, T as T2000Options, S as SendResult, B as BalanceResponse, a as TransactionRecord, D as DepositInfo, L as LendingAdapter, b as SwapAdapter, c as SaveResult, W as WithdrawResult, M as MaxWithdrawResult, d as BorrowResult, R as RepayResult, e as MaxBorrowResult, H as HealthFactorResult, f as SwapResult, g as InvestResult, P as PortfolioResult, h as PositionsResult, i as RatesResult, j as LendingRates, k as RebalanceResult, E as EarningsResult, F as FundStatusResult, l as SentinelAgent, m as SentinelAttackResult, G as GasMethod } from './index-B14ZyQZt.cjs';
|
|
5
|
-
export { A as AdapterCapability, n as AdapterPositions, o as AdapterTxResult, p as AssetRates, C as CetusAdapter, q as GasReserve, r as HealthInfo, s as InvestmentPosition, N as NaviAdapter, t as PerpsAdapter, u as PerpsPosition, v as PositionEntry, w as PositionSide, x as ProtocolDescriptor, y as ProtocolRegistry, z as RebalanceStep, J as SentinelVerdict, K as SuilendAdapter, O as SwapQuote, Q as TradePositionsResult, U as TradeResult, V as allDescriptors, X as cetusDescriptor, Y as getSentinelInfo, Z as listSentinels, _ as naviDescriptor, $ as requestAttack, a0 as sentinelAttack, a1 as sentinelDescriptor, a2 as settleAttack, a3 as submitPrompt, a4 as suilendDescriptor } from './index-B14ZyQZt.cjs';
|
|
6
6
|
import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
|
|
7
7
|
|
|
8
|
-
type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'CONTACT_NOT_FOUND' | 'INVALID_CONTACT_NAME' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'SAFEGUARD_BLOCKED' | 'INSUFFICIENT_INVESTMENT' | 'INVESTMENT_LOCKED' | 'MARKET_NOT_SUPPORTED' | 'LEVERAGE_EXCEEDED' | 'POSITION_SIZE_EXCEEDED' | 'BLUEFIN_AUTH_FAILED' | 'BLUEFIN_API_ERROR' | 'POSITION_NOT_FOUND' | 'UNKNOWN';
|
|
8
|
+
type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'CONTACT_NOT_FOUND' | 'INVALID_CONTACT_NAME' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'SAFEGUARD_BLOCKED' | 'INSUFFICIENT_INVESTMENT' | 'INVESTMENT_LOCKED' | 'INVEST_ALREADY_EARNING' | 'INVEST_NOT_EARNING' | 'BORROW_GUARD_INVESTMENT' | 'MARKET_NOT_SUPPORTED' | 'LEVERAGE_EXCEEDED' | 'POSITION_SIZE_EXCEEDED' | 'BLUEFIN_AUTH_FAILED' | 'BLUEFIN_API_ERROR' | 'POSITION_NOT_FOUND' | 'UNKNOWN';
|
|
9
9
|
interface T2000ErrorData {
|
|
10
10
|
reason?: string;
|
|
11
11
|
[key: string]: unknown;
|
|
@@ -62,7 +62,7 @@ declare const SUPPORTED_ASSETS: {
|
|
|
62
62
|
readonly displayName: "SUI";
|
|
63
63
|
};
|
|
64
64
|
readonly BTC: {
|
|
65
|
-
readonly type: "
|
|
65
|
+
readonly type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC";
|
|
66
66
|
readonly decimals: 8;
|
|
67
67
|
readonly symbol: "BTC";
|
|
68
68
|
readonly displayName: "Bitcoin";
|
|
@@ -84,7 +84,7 @@ declare const INVESTMENT_ASSETS: {
|
|
|
84
84
|
readonly displayName: "SUI";
|
|
85
85
|
};
|
|
86
86
|
readonly BTC: {
|
|
87
|
-
readonly type: "
|
|
87
|
+
readonly type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC";
|
|
88
88
|
readonly decimals: 8;
|
|
89
89
|
readonly symbol: "BTC";
|
|
90
90
|
readonly displayName: "Bitcoin";
|
|
@@ -177,6 +177,9 @@ interface StoredPosition {
|
|
|
177
177
|
costBasis: number;
|
|
178
178
|
avgPrice: number;
|
|
179
179
|
trades: InvestmentTrade[];
|
|
180
|
+
earning?: boolean;
|
|
181
|
+
earningProtocol?: string;
|
|
182
|
+
earningApy?: number;
|
|
180
183
|
}
|
|
181
184
|
declare class PortfolioManager {
|
|
182
185
|
private data;
|
|
@@ -191,6 +194,9 @@ declare class PortfolioManager {
|
|
|
191
194
|
getPositions(): Array<{
|
|
192
195
|
asset: string;
|
|
193
196
|
} & StoredPosition>;
|
|
197
|
+
recordEarn(asset: string, protocol: string, apy: number): void;
|
|
198
|
+
recordUnearn(asset: string): void;
|
|
199
|
+
isEarning(asset: string): boolean;
|
|
194
200
|
getRealizedPnL(): number;
|
|
195
201
|
}
|
|
196
202
|
|
|
@@ -287,6 +293,7 @@ declare class T2000 extends EventEmitter<T2000Events> {
|
|
|
287
293
|
private _swapFromUsdc;
|
|
288
294
|
private _convertWalletStablesToUsdc;
|
|
289
295
|
maxWithdraw(): Promise<MaxWithdrawResult>;
|
|
296
|
+
private adjustMaxBorrowForInvestments;
|
|
290
297
|
borrow(params: {
|
|
291
298
|
amount: number;
|
|
292
299
|
protocol?: string;
|
|
@@ -328,6 +335,12 @@ declare class T2000 extends EventEmitter<T2000Events> {
|
|
|
328
335
|
usdAmount: number | 'all';
|
|
329
336
|
maxSlippage?: number;
|
|
330
337
|
}): Promise<InvestResult>;
|
|
338
|
+
investEarn(params: {
|
|
339
|
+
asset: InvestmentAsset;
|
|
340
|
+
}): Promise<InvestEarnResult>;
|
|
341
|
+
investUnearn(params: {
|
|
342
|
+
asset: InvestmentAsset;
|
|
343
|
+
}): Promise<InvestEarnResult>;
|
|
331
344
|
getPortfolio(): Promise<PortfolioResult>;
|
|
332
345
|
positions(): Promise<PositionsResult>;
|
|
333
346
|
rates(): Promise<RatesResult>;
|
|
@@ -478,4 +491,4 @@ interface GasStatusResponse {
|
|
|
478
491
|
}
|
|
479
492
|
declare function getGasStatus(address?: string): Promise<GasStatusResponse>;
|
|
480
493
|
|
|
481
|
-
export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, type Contact, ContactManager, type ContactMap, DEFAULT_MAX_LEVERAGE, DEFAULT_MAX_POSITION_SIZE, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, GAS_RESERVE_MIN, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, INVESTMENT_ASSETS, InvestResult, type InvestmentAsset, InvestmentTrade, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, OUTBOUND_OPS, PERPS_MARKETS, type PerpsMarket, PortfolioManager, PortfolioResult, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, type SafeguardConfig, SafeguardEnforcer, SafeguardError, type SafeguardErrorDetails, type SafeguardRule, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, type TxMetadata, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatAssetAmount, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
|
|
494
|
+
export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, type Contact, ContactManager, type ContactMap, DEFAULT_MAX_LEVERAGE, DEFAULT_MAX_POSITION_SIZE, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, GAS_RESERVE_MIN, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, INVESTMENT_ASSETS, InvestEarnResult, InvestResult, type InvestmentAsset, InvestmentTrade, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, OUTBOUND_OPS, PERPS_MARKETS, type PerpsMarket, PortfolioManager, PortfolioResult, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, type SafeguardConfig, SafeguardEnforcer, SafeguardError, type SafeguardErrorDetails, type SafeguardRule, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, type TxMetadata, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatAssetAmount, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { I as InvestmentTrade, T as T2000Options, S as SendResult, B as BalanceResponse, a as TransactionRecord, D as DepositInfo, L as LendingAdapter, b as SwapAdapter, c as SaveResult, W as WithdrawResult, M as MaxWithdrawResult, d as BorrowResult, R as RepayResult, e as MaxBorrowResult, H as HealthFactorResult, f as SwapResult, g as InvestResult, h as InvestEarnResult, P as PortfolioResult, i as PositionsResult, j as RatesResult, k as LendingRates, l as RebalanceResult, E as EarningsResult, F as FundStatusResult, m as SentinelAgent, n as SentinelAttackResult, G as GasMethod } from './index-BykavuDO.js';
|
|
2
|
+
export { A as AdapterCapability, o as AdapterPositions, p as AdapterTxResult, q as AssetRates, C as CetusAdapter, r as GasReserve, s as HealthInfo, t as InvestmentPosition, N as NaviAdapter, u as PerpsAdapter, v as PerpsPosition, w as PositionEntry, x as PositionSide, y as ProtocolDescriptor, z as ProtocolRegistry, J as RebalanceStep, K as SentinelVerdict, O as SuilendAdapter, Q as SwapQuote, U as TradePositionsResult, V as TradeResult, X as allDescriptors, Y as cetusDescriptor, Z as getSentinelInfo, _ as listSentinels, $ as naviDescriptor, a0 as requestAttack, a1 as sentinelAttack, a2 as sentinelDescriptor, a3 as settleAttack, a4 as submitPrompt, a5 as suilendDescriptor } from './index-BykavuDO.js';
|
|
1
3
|
import { EventEmitter } from 'eventemitter3';
|
|
2
4
|
import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
|
|
3
5
|
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
4
|
-
import { I as InvestmentTrade, T as T2000Options, S as SendResult, B as BalanceResponse, a as TransactionRecord, D as DepositInfo, L as LendingAdapter, b as SwapAdapter, c as SaveResult, W as WithdrawResult, M as MaxWithdrawResult, d as BorrowResult, R as RepayResult, e as MaxBorrowResult, H as HealthFactorResult, f as SwapResult, g as InvestResult, P as PortfolioResult, h as PositionsResult, i as RatesResult, j as LendingRates, k as RebalanceResult, E as EarningsResult, F as FundStatusResult, l as SentinelAgent, m as SentinelAttackResult, G as GasMethod } from './index-B14ZyQZt.js';
|
|
5
|
-
export { A as AdapterCapability, n as AdapterPositions, o as AdapterTxResult, p as AssetRates, C as CetusAdapter, q as GasReserve, r as HealthInfo, s as InvestmentPosition, N as NaviAdapter, t as PerpsAdapter, u as PerpsPosition, v as PositionEntry, w as PositionSide, x as ProtocolDescriptor, y as ProtocolRegistry, z as RebalanceStep, J as SentinelVerdict, K as SuilendAdapter, O as SwapQuote, Q as TradePositionsResult, U as TradeResult, V as allDescriptors, X as cetusDescriptor, Y as getSentinelInfo, Z as listSentinels, _ as naviDescriptor, $ as requestAttack, a0 as sentinelAttack, a1 as sentinelDescriptor, a2 as settleAttack, a3 as submitPrompt, a4 as suilendDescriptor } from './index-B14ZyQZt.js';
|
|
6
6
|
import { Transaction, TransactionObjectArgument } from '@mysten/sui/transactions';
|
|
7
7
|
|
|
8
|
-
type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'CONTACT_NOT_FOUND' | 'INVALID_CONTACT_NAME' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'SAFEGUARD_BLOCKED' | 'INSUFFICIENT_INVESTMENT' | 'INVESTMENT_LOCKED' | 'MARKET_NOT_SUPPORTED' | 'LEVERAGE_EXCEEDED' | 'POSITION_SIZE_EXCEEDED' | 'BLUEFIN_AUTH_FAILED' | 'BLUEFIN_API_ERROR' | 'POSITION_NOT_FOUND' | 'UNKNOWN';
|
|
8
|
+
type T2000ErrorCode = 'INSUFFICIENT_BALANCE' | 'INSUFFICIENT_GAS' | 'INVALID_ADDRESS' | 'INVALID_AMOUNT' | 'WALLET_NOT_FOUND' | 'WALLET_LOCKED' | 'WALLET_EXISTS' | 'SPONSOR_FAILED' | 'SPONSOR_RATE_LIMITED' | 'GAS_STATION_UNAVAILABLE' | 'GAS_FEE_EXCEEDED' | 'SIMULATION_FAILED' | 'TRANSACTION_FAILED' | 'ASSET_NOT_SUPPORTED' | 'SWAP_FAILED' | 'SLIPPAGE_EXCEEDED' | 'HEALTH_FACTOR_TOO_LOW' | 'WITHDRAW_WOULD_LIQUIDATE' | 'NO_COLLATERAL' | 'PROTOCOL_PAUSED' | 'PROTOCOL_UNAVAILABLE' | 'RPC_ERROR' | 'RPC_UNREACHABLE' | 'SPONSOR_UNAVAILABLE' | 'AUTO_TOPUP_FAILED' | 'PRICE_EXCEEDS_LIMIT' | 'UNSUPPORTED_NETWORK' | 'PAYMENT_EXPIRED' | 'DUPLICATE_PAYMENT' | 'FACILITATOR_REJECTION' | 'CONTACT_NOT_FOUND' | 'INVALID_CONTACT_NAME' | 'FACILITATOR_TIMEOUT' | 'SENTINEL_API_ERROR' | 'SENTINEL_NOT_FOUND' | 'SENTINEL_TX_FAILED' | 'SENTINEL_TEE_ERROR' | 'SAFEGUARD_BLOCKED' | 'INSUFFICIENT_INVESTMENT' | 'INVESTMENT_LOCKED' | 'INVEST_ALREADY_EARNING' | 'INVEST_NOT_EARNING' | 'BORROW_GUARD_INVESTMENT' | 'MARKET_NOT_SUPPORTED' | 'LEVERAGE_EXCEEDED' | 'POSITION_SIZE_EXCEEDED' | 'BLUEFIN_AUTH_FAILED' | 'BLUEFIN_API_ERROR' | 'POSITION_NOT_FOUND' | 'UNKNOWN';
|
|
9
9
|
interface T2000ErrorData {
|
|
10
10
|
reason?: string;
|
|
11
11
|
[key: string]: unknown;
|
|
@@ -62,7 +62,7 @@ declare const SUPPORTED_ASSETS: {
|
|
|
62
62
|
readonly displayName: "SUI";
|
|
63
63
|
};
|
|
64
64
|
readonly BTC: {
|
|
65
|
-
readonly type: "
|
|
65
|
+
readonly type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC";
|
|
66
66
|
readonly decimals: 8;
|
|
67
67
|
readonly symbol: "BTC";
|
|
68
68
|
readonly displayName: "Bitcoin";
|
|
@@ -84,7 +84,7 @@ declare const INVESTMENT_ASSETS: {
|
|
|
84
84
|
readonly displayName: "SUI";
|
|
85
85
|
};
|
|
86
86
|
readonly BTC: {
|
|
87
|
-
readonly type: "
|
|
87
|
+
readonly type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC";
|
|
88
88
|
readonly decimals: 8;
|
|
89
89
|
readonly symbol: "BTC";
|
|
90
90
|
readonly displayName: "Bitcoin";
|
|
@@ -177,6 +177,9 @@ interface StoredPosition {
|
|
|
177
177
|
costBasis: number;
|
|
178
178
|
avgPrice: number;
|
|
179
179
|
trades: InvestmentTrade[];
|
|
180
|
+
earning?: boolean;
|
|
181
|
+
earningProtocol?: string;
|
|
182
|
+
earningApy?: number;
|
|
180
183
|
}
|
|
181
184
|
declare class PortfolioManager {
|
|
182
185
|
private data;
|
|
@@ -191,6 +194,9 @@ declare class PortfolioManager {
|
|
|
191
194
|
getPositions(): Array<{
|
|
192
195
|
asset: string;
|
|
193
196
|
} & StoredPosition>;
|
|
197
|
+
recordEarn(asset: string, protocol: string, apy: number): void;
|
|
198
|
+
recordUnearn(asset: string): void;
|
|
199
|
+
isEarning(asset: string): boolean;
|
|
194
200
|
getRealizedPnL(): number;
|
|
195
201
|
}
|
|
196
202
|
|
|
@@ -287,6 +293,7 @@ declare class T2000 extends EventEmitter<T2000Events> {
|
|
|
287
293
|
private _swapFromUsdc;
|
|
288
294
|
private _convertWalletStablesToUsdc;
|
|
289
295
|
maxWithdraw(): Promise<MaxWithdrawResult>;
|
|
296
|
+
private adjustMaxBorrowForInvestments;
|
|
290
297
|
borrow(params: {
|
|
291
298
|
amount: number;
|
|
292
299
|
protocol?: string;
|
|
@@ -328,6 +335,12 @@ declare class T2000 extends EventEmitter<T2000Events> {
|
|
|
328
335
|
usdAmount: number | 'all';
|
|
329
336
|
maxSlippage?: number;
|
|
330
337
|
}): Promise<InvestResult>;
|
|
338
|
+
investEarn(params: {
|
|
339
|
+
asset: InvestmentAsset;
|
|
340
|
+
}): Promise<InvestEarnResult>;
|
|
341
|
+
investUnearn(params: {
|
|
342
|
+
asset: InvestmentAsset;
|
|
343
|
+
}): Promise<InvestEarnResult>;
|
|
331
344
|
getPortfolio(): Promise<PortfolioResult>;
|
|
332
345
|
positions(): Promise<PositionsResult>;
|
|
333
346
|
rates(): Promise<RatesResult>;
|
|
@@ -478,4 +491,4 @@ interface GasStatusResponse {
|
|
|
478
491
|
}
|
|
479
492
|
declare function getGasStatus(address?: string): Promise<GasStatusResponse>;
|
|
480
493
|
|
|
481
|
-
export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, type Contact, ContactManager, type ContactMap, DEFAULT_MAX_LEVERAGE, DEFAULT_MAX_POSITION_SIZE, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, GAS_RESERVE_MIN, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, INVESTMENT_ASSETS, InvestResult, type InvestmentAsset, InvestmentTrade, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, OUTBOUND_OPS, PERPS_MARKETS, type PerpsMarket, PortfolioManager, PortfolioResult, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, type SafeguardConfig, SafeguardEnforcer, SafeguardError, type SafeguardErrorDetails, type SafeguardRule, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, type TxMetadata, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatAssetAmount, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
|
|
494
|
+
export { type AutoTopUpResult, BPS_DENOMINATOR, BalanceResponse, BorrowResult, CLOCK_ID, type Contact, ContactManager, type ContactMap, DEFAULT_MAX_LEVERAGE, DEFAULT_MAX_POSITION_SIZE, DEFAULT_NETWORK, DEFAULT_SAFEGUARD_CONFIG, DepositInfo, EarningsResult, type FeeOperation, FundStatusResult, GAS_RESERVE_MIN, type GasExecutionResult, GasMethod, type GasRequestType, type GasSponsorResponse, type GasStatusResponse, HealthFactorResult, INVESTMENT_ASSETS, InvestEarnResult, InvestResult, type InvestmentAsset, InvestmentTrade, LendingAdapter, LendingRates, MIST_PER_SUI, MaxBorrowResult, MaxWithdrawResult, OUTBOUND_OPS, PERPS_MARKETS, type PerpsMarket, PortfolioManager, PortfolioResult, PositionsResult, type ProtocolFeeInfo, RatesResult, RebalanceResult, RepayResult, SENTINEL, SUI_DECIMALS, SUPPORTED_ASSETS, type SafeguardConfig, SafeguardEnforcer, SafeguardError, type SafeguardErrorDetails, type SafeguardRule, SaveResult, SendResult, SentinelAgent, SentinelAttackResult, type SimulationResult, type SupportedAsset, SwapAdapter, SwapResult, T2000, T2000Error, type T2000ErrorCode, type T2000ErrorData, T2000Options, TransactionRecord, type TxMetadata, USDC_DECIMALS, WithdrawResult, addCollectFeeToTx, calculateFee, executeAutoTopUp, executeWithGas, exportPrivateKey, formatAssetAmount, formatSui, formatUsd, generateKeypair, getAddress, getDecimals, getGasStatus, getPoolPrice, getRates, keypairFromPrivateKey, loadKey, mapMoveAbortCode, mapWalletError, mistToSui, rawToStable, rawToUsdc, saveKey, shouldAutoTopUp, simulateTransaction, solveHashcash, stableToRaw, suiToMist, throwIfSimulationFailed, truncateAddress, usdcToRaw, validateAddress, walletExists };
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ var SUPPORTED_ASSETS = {
|
|
|
58
58
|
displayName: "SUI"
|
|
59
59
|
},
|
|
60
60
|
BTC: {
|
|
61
|
-
type: "
|
|
61
|
+
type: "0x0041f9f9344cac094454cd574e333c4fdb132d7bcc9379bcd4aab485b2a63942::wbtc::WBTC",
|
|
62
62
|
decimals: 8,
|
|
63
63
|
symbol: "BTC",
|
|
64
64
|
displayName: "Bitcoin"
|
|
@@ -600,16 +600,23 @@ function resolvePoolSymbol(pool) {
|
|
|
600
600
|
}
|
|
601
601
|
return pool.token?.symbol ?? "UNKNOWN";
|
|
602
602
|
}
|
|
603
|
+
function resolveAssetInfo(asset) {
|
|
604
|
+
if (asset in SUPPORTED_ASSETS) {
|
|
605
|
+
const info = SUPPORTED_ASSETS[asset];
|
|
606
|
+
return { type: info.type, decimals: info.decimals, displayName: info.displayName };
|
|
607
|
+
}
|
|
608
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `Unknown asset: ${asset}`);
|
|
609
|
+
}
|
|
603
610
|
async function getPool(asset = "USDC") {
|
|
604
611
|
const pools = await getPools();
|
|
605
|
-
const targetType =
|
|
612
|
+
const { type: targetType, displayName } = resolveAssetInfo(asset);
|
|
606
613
|
const pool = pools.find(
|
|
607
614
|
(p) => matchesCoinType(p.suiCoinType || p.coinType || "", targetType)
|
|
608
615
|
);
|
|
609
616
|
if (!pool) {
|
|
610
617
|
throw new T2000Error(
|
|
611
618
|
"ASSET_NOT_SUPPORTED",
|
|
612
|
-
`${
|
|
619
|
+
`${displayName} pool not found on NAVI`
|
|
613
620
|
);
|
|
614
621
|
}
|
|
615
622
|
return pool;
|
|
@@ -632,13 +639,14 @@ function addOracleUpdate(tx, config, pool) {
|
|
|
632
639
|
]
|
|
633
640
|
});
|
|
634
641
|
}
|
|
635
|
-
function
|
|
636
|
-
const
|
|
637
|
-
const
|
|
642
|
+
function refreshOracles(tx, config, pools, opts) {
|
|
643
|
+
const assetsToRefresh = NAVI_SUPPORTED_ASSETS;
|
|
644
|
+
const targetTypes = assetsToRefresh.map((a) => SUPPORTED_ASSETS[a].type);
|
|
645
|
+
const matchedPools = pools.filter((p) => {
|
|
638
646
|
const ct = p.suiCoinType || p.coinType || "";
|
|
639
|
-
return
|
|
647
|
+
return targetTypes.some((t) => matchesCoinType(ct, t));
|
|
640
648
|
});
|
|
641
|
-
for (const pool of
|
|
649
|
+
for (const pool of matchedPools) {
|
|
642
650
|
addOracleUpdate(tx, config, pool);
|
|
643
651
|
}
|
|
644
652
|
}
|
|
@@ -712,7 +720,7 @@ async function buildSaveTx(client, address, amount, options = {}) {
|
|
|
712
720
|
throw new T2000Error("INVALID_AMOUNT", "Save amount must be a positive number");
|
|
713
721
|
}
|
|
714
722
|
const asset = options.asset ?? "USDC";
|
|
715
|
-
const assetInfo =
|
|
723
|
+
const assetInfo = resolveAssetInfo(asset);
|
|
716
724
|
const rawAmount = Number(stableToRaw(amount, assetInfo.decimals));
|
|
717
725
|
const [config, pool] = await Promise.all([getConfig(), getPool(asset)]);
|
|
718
726
|
const coins = await fetchCoins(client, address, assetInfo.type);
|
|
@@ -741,7 +749,7 @@ async function buildSaveTx(client, address, amount, options = {}) {
|
|
|
741
749
|
}
|
|
742
750
|
async function buildWithdrawTx(client, address, amount, options = {}) {
|
|
743
751
|
const asset = options.asset ?? "USDC";
|
|
744
|
-
const assetInfo =
|
|
752
|
+
const assetInfo = resolveAssetInfo(asset);
|
|
745
753
|
const [config, pool, pools, states] = await Promise.all([
|
|
746
754
|
getConfig(),
|
|
747
755
|
getPool(asset),
|
|
@@ -758,7 +766,7 @@ async function buildWithdrawTx(client, address, amount, options = {}) {
|
|
|
758
766
|
}
|
|
759
767
|
const tx = new Transaction();
|
|
760
768
|
tx.setSender(address);
|
|
761
|
-
|
|
769
|
+
refreshOracles(tx, config, pools);
|
|
762
770
|
const [balance] = tx.moveCall({
|
|
763
771
|
target: `${config.package}::incentive_v3::withdraw_v2`,
|
|
764
772
|
arguments: [
|
|
@@ -784,7 +792,7 @@ async function buildWithdrawTx(client, address, amount, options = {}) {
|
|
|
784
792
|
}
|
|
785
793
|
async function addWithdrawToTx(tx, client, address, amount, options = {}) {
|
|
786
794
|
const asset = options.asset ?? "USDC";
|
|
787
|
-
const assetInfo =
|
|
795
|
+
const assetInfo = resolveAssetInfo(asset);
|
|
788
796
|
const [config, pool, pools, states] = await Promise.all([
|
|
789
797
|
getConfig(),
|
|
790
798
|
getPool(asset),
|
|
@@ -803,7 +811,7 @@ async function addWithdrawToTx(tx, client, address, amount, options = {}) {
|
|
|
803
811
|
});
|
|
804
812
|
return { coin: coin2, effectiveAmount: 0 };
|
|
805
813
|
}
|
|
806
|
-
|
|
814
|
+
refreshOracles(tx, config, pools);
|
|
807
815
|
const [balance] = tx.moveCall({
|
|
808
816
|
target: `${config.package}::incentive_v3::withdraw_v2`,
|
|
809
817
|
arguments: [
|
|
@@ -852,7 +860,7 @@ async function addSaveToTx(tx, _client, _address, coin, options = {}) {
|
|
|
852
860
|
typeArguments: [pool.suiCoinType]
|
|
853
861
|
});
|
|
854
862
|
}
|
|
855
|
-
async function addRepayToTx(tx,
|
|
863
|
+
async function addRepayToTx(tx, _client, _address, coin, options = {}) {
|
|
856
864
|
const asset = options.asset ?? "USDC";
|
|
857
865
|
const [config, pool] = await Promise.all([getConfig(), getPool(asset)]);
|
|
858
866
|
addOracleUpdate(tx, config, pool);
|
|
@@ -882,7 +890,7 @@ async function buildBorrowTx(client, address, amount, options = {}) {
|
|
|
882
890
|
throw new T2000Error("INVALID_AMOUNT", "Borrow amount must be a positive number");
|
|
883
891
|
}
|
|
884
892
|
const asset = options.asset ?? "USDC";
|
|
885
|
-
const assetInfo =
|
|
893
|
+
const assetInfo = resolveAssetInfo(asset);
|
|
886
894
|
const rawAmount = Number(stableToRaw(amount, assetInfo.decimals));
|
|
887
895
|
const [config, pool, pools] = await Promise.all([
|
|
888
896
|
getConfig(),
|
|
@@ -891,7 +899,7 @@ async function buildBorrowTx(client, address, amount, options = {}) {
|
|
|
891
899
|
]);
|
|
892
900
|
const tx = new Transaction();
|
|
893
901
|
tx.setSender(address);
|
|
894
|
-
|
|
902
|
+
refreshOracles(tx, config, pools);
|
|
895
903
|
const [balance] = tx.moveCall({
|
|
896
904
|
target: `${config.package}::incentive_v3::borrow_v2`,
|
|
897
905
|
arguments: [
|
|
@@ -923,7 +931,7 @@ async function buildRepayTx(client, address, amount, options = {}) {
|
|
|
923
931
|
throw new T2000Error("INVALID_AMOUNT", "Repay amount must be a positive number");
|
|
924
932
|
}
|
|
925
933
|
const asset = options.asset ?? "USDC";
|
|
926
|
-
const assetInfo =
|
|
934
|
+
const assetInfo = resolveAssetInfo(asset);
|
|
927
935
|
const rawAmount = Number(stableToRaw(amount, assetInfo.decimals));
|
|
928
936
|
const [config, pool] = await Promise.all([getConfig(), getPool(asset)]);
|
|
929
937
|
const coins = await fetchCoins(client, address, assetInfo.type);
|
|
@@ -1022,11 +1030,12 @@ async function getHealthFactor(client, addressOrKeypair) {
|
|
|
1022
1030
|
liquidationThreshold: liqThreshold
|
|
1023
1031
|
};
|
|
1024
1032
|
}
|
|
1033
|
+
var NAVI_SUPPORTED_ASSETS = [...STABLE_ASSETS, "SUI", "ETH"];
|
|
1025
1034
|
async function getRates(client) {
|
|
1026
1035
|
try {
|
|
1027
1036
|
const pools = await getPools();
|
|
1028
1037
|
const result = {};
|
|
1029
|
-
for (const asset of
|
|
1038
|
+
for (const asset of NAVI_SUPPORTED_ASSETS) {
|
|
1030
1039
|
const targetType = SUPPORTED_ASSETS[asset].type;
|
|
1031
1040
|
const pool = pools.find((p) => matchesCoinType(p.suiCoinType || p.coinType || "", targetType));
|
|
1032
1041
|
if (!pool) continue;
|
|
@@ -1396,7 +1405,11 @@ var ProtocolRegistry = class {
|
|
|
1396
1405
|
}
|
|
1397
1406
|
async allRatesAcrossAssets() {
|
|
1398
1407
|
const results = [];
|
|
1399
|
-
|
|
1408
|
+
const allAssets = [...STABLE_ASSETS, ...Object.keys(INVESTMENT_ASSETS)];
|
|
1409
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1410
|
+
for (const asset of allAssets) {
|
|
1411
|
+
if (seen.has(asset)) continue;
|
|
1412
|
+
seen.add(asset);
|
|
1400
1413
|
for (const adapter of this.lending.values()) {
|
|
1401
1414
|
if (!adapter.supportedAssets.includes(asset)) continue;
|
|
1402
1415
|
try {
|
|
@@ -1471,7 +1484,7 @@ var NaviAdapter = class {
|
|
|
1471
1484
|
name = "NAVI Protocol";
|
|
1472
1485
|
version = "1.0.0";
|
|
1473
1486
|
capabilities = ["save", "withdraw", "borrow", "repay"];
|
|
1474
|
-
supportedAssets = [...STABLE_ASSETS];
|
|
1487
|
+
supportedAssets = [...STABLE_ASSETS, "SUI", "ETH"];
|
|
1475
1488
|
supportsSameAssetBorrow = true;
|
|
1476
1489
|
client;
|
|
1477
1490
|
async init(client) {
|
|
@@ -1498,23 +1511,23 @@ var NaviAdapter = class {
|
|
|
1498
1511
|
return getHealthFactor(this.client, address);
|
|
1499
1512
|
}
|
|
1500
1513
|
async buildSaveTx(address, amount, asset, options) {
|
|
1501
|
-
const
|
|
1502
|
-
const tx = await buildSaveTx(this.client, address, amount, { ...options, asset:
|
|
1514
|
+
const normalized = normalizeAsset(asset);
|
|
1515
|
+
const tx = await buildSaveTx(this.client, address, amount, { ...options, asset: normalized });
|
|
1503
1516
|
return { tx };
|
|
1504
1517
|
}
|
|
1505
1518
|
async buildWithdrawTx(address, amount, asset) {
|
|
1506
|
-
const
|
|
1507
|
-
const result = await buildWithdrawTx(this.client, address, amount, { asset:
|
|
1519
|
+
const normalized = normalizeAsset(asset);
|
|
1520
|
+
const result = await buildWithdrawTx(this.client, address, amount, { asset: normalized });
|
|
1508
1521
|
return { tx: result.tx, effectiveAmount: result.effectiveAmount };
|
|
1509
1522
|
}
|
|
1510
1523
|
async buildBorrowTx(address, amount, asset, options) {
|
|
1511
|
-
const
|
|
1512
|
-
const tx = await buildBorrowTx(this.client, address, amount, { ...options, asset:
|
|
1524
|
+
const normalized = normalizeAsset(asset);
|
|
1525
|
+
const tx = await buildBorrowTx(this.client, address, amount, { ...options, asset: normalized });
|
|
1513
1526
|
return { tx };
|
|
1514
1527
|
}
|
|
1515
1528
|
async buildRepayTx(address, amount, asset) {
|
|
1516
|
-
const
|
|
1517
|
-
const tx = await buildRepayTx(this.client, address, amount, { asset:
|
|
1529
|
+
const normalized = normalizeAsset(asset);
|
|
1530
|
+
const tx = await buildRepayTx(this.client, address, amount, { asset: normalized });
|
|
1518
1531
|
return { tx };
|
|
1519
1532
|
}
|
|
1520
1533
|
async maxWithdraw(address, _asset) {
|
|
@@ -1524,16 +1537,16 @@ var NaviAdapter = class {
|
|
|
1524
1537
|
return maxBorrowAmount(this.client, address);
|
|
1525
1538
|
}
|
|
1526
1539
|
async addWithdrawToTx(tx, address, amount, asset) {
|
|
1527
|
-
const
|
|
1528
|
-
return addWithdrawToTx(tx, this.client, address, amount, { asset:
|
|
1540
|
+
const normalized = normalizeAsset(asset);
|
|
1541
|
+
return addWithdrawToTx(tx, this.client, address, amount, { asset: normalized });
|
|
1529
1542
|
}
|
|
1530
1543
|
async addSaveToTx(tx, address, coin, asset, options) {
|
|
1531
|
-
const
|
|
1532
|
-
return addSaveToTx(tx, this.client, address, coin, { ...options, asset:
|
|
1544
|
+
const normalized = normalizeAsset(asset);
|
|
1545
|
+
return addSaveToTx(tx, this.client, address, coin, { ...options, asset: normalized });
|
|
1533
1546
|
}
|
|
1534
1547
|
async addRepayToTx(tx, address, coin, asset) {
|
|
1535
|
-
const
|
|
1536
|
-
return addRepayToTx(tx, this.client, address, coin, { asset:
|
|
1548
|
+
const normalized = normalizeAsset(asset);
|
|
1549
|
+
return addRepayToTx(tx, this.client, address, coin, { asset: normalized });
|
|
1537
1550
|
}
|
|
1538
1551
|
};
|
|
1539
1552
|
var DEFAULT_SLIPPAGE_BPS = 300;
|
|
@@ -1845,7 +1858,7 @@ var SuilendAdapter = class {
|
|
|
1845
1858
|
name = "Suilend";
|
|
1846
1859
|
version = "2.0.0";
|
|
1847
1860
|
capabilities = ["save", "withdraw", "borrow", "repay"];
|
|
1848
|
-
supportedAssets = [...STABLE_ASSETS];
|
|
1861
|
+
supportedAssets = [...STABLE_ASSETS, "SUI", "ETH", "BTC"];
|
|
1849
1862
|
supportsSameAssetBorrow = false;
|
|
1850
1863
|
client;
|
|
1851
1864
|
publishedAt = null;
|
|
@@ -2867,6 +2880,38 @@ var PortfolioManager = class {
|
|
|
2867
2880
|
this.load();
|
|
2868
2881
|
return Object.entries(this.data.positions).filter(([, pos]) => pos.totalAmount > 0).map(([asset, pos]) => ({ asset, ...pos }));
|
|
2869
2882
|
}
|
|
2883
|
+
recordEarn(asset, protocol, apy) {
|
|
2884
|
+
this.load();
|
|
2885
|
+
const pos = this.data.positions[asset];
|
|
2886
|
+
if (!pos || pos.totalAmount <= 0) {
|
|
2887
|
+
throw new T2000Error("INSUFFICIENT_INVESTMENT", `No ${asset} position to earn on`);
|
|
2888
|
+
}
|
|
2889
|
+
if (pos.earning) {
|
|
2890
|
+
throw new T2000Error("INVEST_ALREADY_EARNING", `${asset} is already earning via ${pos.earningProtocol}`);
|
|
2891
|
+
}
|
|
2892
|
+
pos.earning = true;
|
|
2893
|
+
pos.earningProtocol = protocol;
|
|
2894
|
+
pos.earningApy = apy;
|
|
2895
|
+
this.data.positions[asset] = pos;
|
|
2896
|
+
this.save();
|
|
2897
|
+
}
|
|
2898
|
+
recordUnearn(asset) {
|
|
2899
|
+
this.load();
|
|
2900
|
+
const pos = this.data.positions[asset];
|
|
2901
|
+
if (!pos || !pos.earning) {
|
|
2902
|
+
throw new T2000Error("INVEST_NOT_EARNING", `${asset} is not currently earning`);
|
|
2903
|
+
}
|
|
2904
|
+
pos.earning = false;
|
|
2905
|
+
pos.earningProtocol = void 0;
|
|
2906
|
+
pos.earningApy = void 0;
|
|
2907
|
+
this.data.positions[asset] = pos;
|
|
2908
|
+
this.save();
|
|
2909
|
+
}
|
|
2910
|
+
isEarning(asset) {
|
|
2911
|
+
this.load();
|
|
2912
|
+
const pos = this.data.positions[asset];
|
|
2913
|
+
return pos?.earning === true;
|
|
2914
|
+
}
|
|
2870
2915
|
getRealizedPnL() {
|
|
2871
2916
|
this.load();
|
|
2872
2917
|
return this.data.realizedPnL;
|
|
@@ -3426,16 +3471,49 @@ To access invested funds: t2000 invest sell ${params.amount} ${asset}`,
|
|
|
3426
3471
|
return adapter.maxWithdraw(this._address, "USDC");
|
|
3427
3472
|
}
|
|
3428
3473
|
// -- Borrowing --
|
|
3474
|
+
async adjustMaxBorrowForInvestments(adapter, maxResult) {
|
|
3475
|
+
const earningPositions = this.portfolio.getPositions().filter((p) => p.earning);
|
|
3476
|
+
if (earningPositions.length === 0) return maxResult;
|
|
3477
|
+
let investmentCollateralUsd = 0;
|
|
3478
|
+
const swapAdapter = this.registry.listSwap()[0];
|
|
3479
|
+
for (const pos of earningPositions) {
|
|
3480
|
+
if (pos.earningProtocol !== adapter.id) continue;
|
|
3481
|
+
try {
|
|
3482
|
+
let price = 0;
|
|
3483
|
+
if (pos.asset === "SUI" && swapAdapter) {
|
|
3484
|
+
price = await swapAdapter.getPoolPrice();
|
|
3485
|
+
} else if (swapAdapter) {
|
|
3486
|
+
const quote = await swapAdapter.getQuote("USDC", pos.asset, 1);
|
|
3487
|
+
price = quote.expectedOutput > 0 ? 1 / quote.expectedOutput : 0;
|
|
3488
|
+
}
|
|
3489
|
+
investmentCollateralUsd += pos.totalAmount * price;
|
|
3490
|
+
} catch {
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
if (investmentCollateralUsd <= 0) return maxResult;
|
|
3494
|
+
const CONSERVATIVE_LTV = 0.6;
|
|
3495
|
+
const investmentBorrowCapacity = investmentCollateralUsd * CONSERVATIVE_LTV;
|
|
3496
|
+
const adjustedMax = Math.max(0, maxResult.maxAmount - investmentBorrowCapacity);
|
|
3497
|
+
return { ...maxResult, maxAmount: adjustedMax };
|
|
3498
|
+
}
|
|
3429
3499
|
async borrow(params) {
|
|
3430
3500
|
this.enforcer.assertNotLocked();
|
|
3431
3501
|
const asset = "USDC";
|
|
3432
3502
|
const adapter = await this.resolveLending(params.protocol, asset, "borrow");
|
|
3433
|
-
const
|
|
3503
|
+
const rawMax = await adapter.maxBorrow(this._address, asset);
|
|
3504
|
+
const maxResult = await this.adjustMaxBorrowForInvestments(adapter, rawMax);
|
|
3434
3505
|
if (maxResult.maxAmount <= 0) {
|
|
3506
|
+
const hasInvestmentEarning = this.portfolio.getPositions().some((p) => p.earning && p.earningProtocol === adapter.id);
|
|
3507
|
+
if (hasInvestmentEarning) {
|
|
3508
|
+
throw new T2000Error(
|
|
3509
|
+
"BORROW_GUARD_INVESTMENT",
|
|
3510
|
+
"Max safe borrow: $0.00. Only savings deposits (stablecoins) count as borrowable collateral. Investment collateral (SUI, ETH, BTC) is excluded."
|
|
3511
|
+
);
|
|
3512
|
+
}
|
|
3435
3513
|
throw new T2000Error("NO_COLLATERAL", "No collateral deposited. Save first with `t2000 save <amount>`.");
|
|
3436
3514
|
}
|
|
3437
3515
|
if (params.amount > maxResult.maxAmount) {
|
|
3438
|
-
throw new T2000Error("HEALTH_FACTOR_TOO_LOW", `Max safe borrow: $${maxResult.maxAmount.toFixed(2)}
|
|
3516
|
+
throw new T2000Error("HEALTH_FACTOR_TOO_LOW", `Max safe borrow: $${maxResult.maxAmount.toFixed(2)}. Only savings deposits count as borrowable collateral.`, {
|
|
3439
3517
|
maxBorrow: maxResult.maxAmount,
|
|
3440
3518
|
currentHF: maxResult.currentHF
|
|
3441
3519
|
});
|
|
@@ -3600,7 +3678,8 @@ To access invested funds: t2000 invest sell ${params.amount} ${asset}`,
|
|
|
3600
3678
|
}
|
|
3601
3679
|
async maxBorrow() {
|
|
3602
3680
|
const adapter = await this.resolveLending(void 0, "USDC", "borrow");
|
|
3603
|
-
|
|
3681
|
+
const rawMax = await adapter.maxBorrow(this._address, "USDC");
|
|
3682
|
+
return this.adjustMaxBorrowForInvestments(adapter, rawMax);
|
|
3604
3683
|
}
|
|
3605
3684
|
async healthFactor() {
|
|
3606
3685
|
const adapter = await this.resolveLending(void 0, "USDC", "save");
|
|
@@ -3764,6 +3843,9 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
|
|
|
3764
3843
|
if (!pos || pos.totalAmount <= 0) {
|
|
3765
3844
|
throw new T2000Error("INSUFFICIENT_INVESTMENT", `No ${params.asset} position to sell`);
|
|
3766
3845
|
}
|
|
3846
|
+
if (pos.earning && pos.earningProtocol) {
|
|
3847
|
+
await this.investUnearn({ asset: params.asset });
|
|
3848
|
+
}
|
|
3767
3849
|
const assetInfo = SUPPORTED_ASSETS[params.asset];
|
|
3768
3850
|
const assetBalance = await this.client.getBalance({
|
|
3769
3851
|
owner: this._address,
|
|
@@ -3838,6 +3920,91 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
|
|
|
3838
3920
|
position
|
|
3839
3921
|
};
|
|
3840
3922
|
}
|
|
3923
|
+
async investEarn(params) {
|
|
3924
|
+
this.enforcer.assertNotLocked();
|
|
3925
|
+
if (!(params.asset in INVESTMENT_ASSETS)) {
|
|
3926
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `${params.asset} is not available for investment`);
|
|
3927
|
+
}
|
|
3928
|
+
const pos = this.portfolio.getPosition(params.asset);
|
|
3929
|
+
if (!pos || pos.totalAmount <= 0) {
|
|
3930
|
+
throw new T2000Error("INSUFFICIENT_INVESTMENT", `No ${params.asset} position to earn on`);
|
|
3931
|
+
}
|
|
3932
|
+
if (pos.earning) {
|
|
3933
|
+
throw new T2000Error("INVEST_ALREADY_EARNING", `${params.asset} is already earning via ${pos.earningProtocol}`);
|
|
3934
|
+
}
|
|
3935
|
+
const { adapter, rate } = await this.registry.bestSaveRate(params.asset);
|
|
3936
|
+
const assetInfo = SUPPORTED_ASSETS[params.asset];
|
|
3937
|
+
const assetBalance = await this.client.getBalance({
|
|
3938
|
+
owner: this._address,
|
|
3939
|
+
coinType: assetInfo.type
|
|
3940
|
+
});
|
|
3941
|
+
const walletAmount = Number(assetBalance.totalBalance) / 10 ** assetInfo.decimals;
|
|
3942
|
+
const gasReserve = params.asset === "SUI" ? GAS_RESERVE_MIN : 0;
|
|
3943
|
+
const depositAmount = Math.max(0, walletAmount - gasReserve);
|
|
3944
|
+
if (depositAmount <= 0) {
|
|
3945
|
+
throw new T2000Error("INSUFFICIENT_BALANCE", `No ${params.asset} available to deposit (wallet: ${walletAmount}, gas reserve: ${gasReserve})`);
|
|
3946
|
+
}
|
|
3947
|
+
const { tx } = await adapter.buildSaveTx(this._address, depositAmount, params.asset);
|
|
3948
|
+
const result = await this.client.signAndExecuteTransaction({
|
|
3949
|
+
signer: this.keypair,
|
|
3950
|
+
transaction: tx,
|
|
3951
|
+
options: { showEffects: true }
|
|
3952
|
+
});
|
|
3953
|
+
await this.client.waitForTransaction({ digest: result.digest });
|
|
3954
|
+
const gasCost = result.effects?.gasUsed ? Math.abs(
|
|
3955
|
+
(Number(result.effects.gasUsed.computationCost) + Number(result.effects.gasUsed.storageCost) - Number(result.effects.gasUsed.storageRebate)) / 1e9
|
|
3956
|
+
) : 0;
|
|
3957
|
+
this.portfolio.recordEarn(params.asset, adapter.id, rate.saveApy);
|
|
3958
|
+
return {
|
|
3959
|
+
success: true,
|
|
3960
|
+
tx: result.digest,
|
|
3961
|
+
asset: params.asset,
|
|
3962
|
+
amount: depositAmount,
|
|
3963
|
+
protocol: adapter.name,
|
|
3964
|
+
apy: rate.saveApy,
|
|
3965
|
+
gasCost,
|
|
3966
|
+
gasMethod: "self-funded"
|
|
3967
|
+
};
|
|
3968
|
+
}
|
|
3969
|
+
async investUnearn(params) {
|
|
3970
|
+
this.enforcer.assertNotLocked();
|
|
3971
|
+
if (!(params.asset in INVESTMENT_ASSETS)) {
|
|
3972
|
+
throw new T2000Error("ASSET_NOT_SUPPORTED", `${params.asset} is not available for investment`);
|
|
3973
|
+
}
|
|
3974
|
+
const pos = this.portfolio.getPosition(params.asset);
|
|
3975
|
+
if (!pos || !pos.earning || !pos.earningProtocol) {
|
|
3976
|
+
throw new T2000Error("INVEST_NOT_EARNING", `${params.asset} is not currently earning`);
|
|
3977
|
+
}
|
|
3978
|
+
const adapter = this.registry.getLending(pos.earningProtocol);
|
|
3979
|
+
if (!adapter) {
|
|
3980
|
+
throw new T2000Error("PROTOCOL_UNAVAILABLE", `Lending protocol ${pos.earningProtocol} not found`);
|
|
3981
|
+
}
|
|
3982
|
+
const positions = await adapter.getPositions(this._address);
|
|
3983
|
+
const supply = positions.supplies.find((s) => s.asset === params.asset);
|
|
3984
|
+
const withdrawAmount = supply?.amount ?? pos.totalAmount;
|
|
3985
|
+
const { tx, effectiveAmount } = await adapter.buildWithdrawTx(this._address, withdrawAmount, params.asset);
|
|
3986
|
+
const result = await this.client.signAndExecuteTransaction({
|
|
3987
|
+
signer: this.keypair,
|
|
3988
|
+
transaction: tx,
|
|
3989
|
+
options: { showEffects: true }
|
|
3990
|
+
});
|
|
3991
|
+
await this.client.waitForTransaction({ digest: result.digest });
|
|
3992
|
+
const gasCost = result.effects?.gasUsed ? Math.abs(
|
|
3993
|
+
(Number(result.effects.gasUsed.computationCost) + Number(result.effects.gasUsed.storageCost) - Number(result.effects.gasUsed.storageRebate)) / 1e9
|
|
3994
|
+
) : 0;
|
|
3995
|
+
const protocolName = adapter.name;
|
|
3996
|
+
this.portfolio.recordUnearn(params.asset);
|
|
3997
|
+
return {
|
|
3998
|
+
success: true,
|
|
3999
|
+
tx: result.digest,
|
|
4000
|
+
asset: params.asset,
|
|
4001
|
+
amount: effectiveAmount,
|
|
4002
|
+
protocol: protocolName,
|
|
4003
|
+
apy: 0,
|
|
4004
|
+
gasCost,
|
|
4005
|
+
gasMethod: "self-funded"
|
|
4006
|
+
};
|
|
4007
|
+
}
|
|
3841
4008
|
async getPortfolio() {
|
|
3842
4009
|
const positions = this.portfolio.getPositions();
|
|
3843
4010
|
const realizedPnL = this.portfolio.getRealizedPnL();
|
|
@@ -3887,7 +4054,10 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
|
|
|
3887
4054
|
currentValue,
|
|
3888
4055
|
unrealizedPnL,
|
|
3889
4056
|
unrealizedPnLPct,
|
|
3890
|
-
trades: pos.trades
|
|
4057
|
+
trades: pos.trades,
|
|
4058
|
+
earning: pos.earning,
|
|
4059
|
+
earningProtocol: pos.earningProtocol,
|
|
4060
|
+
earningApy: pos.earningApy
|
|
3891
4061
|
});
|
|
3892
4062
|
}
|
|
3893
4063
|
const totalInvested = enriched.reduce((sum, p) => sum + p.costBasis, 0);
|
|
@@ -3952,8 +4122,11 @@ To sell investment: t2000 invest sell ${params.amount} ${fromAsset}`,
|
|
|
3952
4122
|
this.registry.allPositions(this._address),
|
|
3953
4123
|
this.registry.allRatesAcrossAssets()
|
|
3954
4124
|
]);
|
|
4125
|
+
const earningAssets = new Set(
|
|
4126
|
+
this.portfolio.getPositions().filter((p) => p.earning).map((p) => p.asset)
|
|
4127
|
+
);
|
|
3955
4128
|
const savePositions = allPositions.flatMap(
|
|
3956
|
-
(p) => p.positions.supplies.filter((s) => s.amount > 0.01).map((s) => ({
|
|
4129
|
+
(p) => p.positions.supplies.filter((s) => s.amount > 0.01).filter((s) => !earningAssets.has(s.asset)).map((s) => ({
|
|
3957
4130
|
protocolId: p.protocolId,
|
|
3958
4131
|
protocol: p.protocol,
|
|
3959
4132
|
asset: s.asset,
|