Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions scripts/testRun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,13 @@ async function deposit() {
network: 'mainnet',
});
const tx = await sdk.deposit({
poolId: '0xccc08b2e42a88002b4bd505e7e0b5bed17079d4cafc2ccbe82da0172d5291867',
poolId: '0x0e1399fe66eca3147766bb113ae7b52b31243874c9e4a64a48e6d8cb91aa3c04',
amount: 10_000n,
address: address,
isAmountA: false,
});
// dryRunTransactionBlock(tx);
executeTransactionBlock(tx);
dryRunTransactionBlock(tx);
// executeTransactionBlock(tx);
}

async function withdraw() {
Expand All @@ -217,7 +217,7 @@ async function withdraw() {
network: 'mainnet',
});
const tx = await sdk.withdraw({
poolId: '0xccc08b2e42a88002b4bd505e7e0b5bed17079d4cafc2ccbe82da0172d5291867',
poolId: '0x0bca47c53d57d203d19611af98a4e723c52cbf1bc58312360bfb5dcba0286de9',
withdrawMax: true,
amount: '5_000_000',
isAmountA: true,
Expand Down
3 changes: 1 addition & 2 deletions src/models/strategyContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,12 +264,11 @@ export class StrategyContext {
return {
poolId: d.pool_id,
packageId: d.package_id,
packageNumber: d.package_number,
strategyType: strategyType,
parentProtocol: d.parent_protocol,
asset: d.asset,
events: {
autocompoundEventType: d.events?.autocompound_event_type,
autocompoundEventType: d.events?.xtoken_ratio_change_event_type,
},
isActive: d.is_active,
poolName: d.pool_name,
Expand Down
180 changes: 180 additions & 0 deletions src/strategies/slushSingleAssetLooping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AlphalendClient } from '@alphafi/alphalend-sdk';
import {
ALPHALEND_LENDING_PROTOCOL_ID,
CLOCK_PACKAGE_ID,
GLOBAL_CONFIGS,
IMAGE_URLS,
VERSIONS,
} from '../utils/constants.js';
Expand Down Expand Up @@ -207,6 +208,15 @@ export class SlushSingleAssetLoopingStrategy extends BaseStrategy<
investor: {
id: this.getStringField(fields.investor, 'id'),
marketId: this.getStringField(fields.investor, 'market_id'),
positionCap: {
clientAddress: this.getNestedField(
fields,
'investor.alphalend_position_cap.client_address',
),
id: this.getNestedField(fields, 'investor.alphalend_position_cap.id'),
imageUrl: this.getNestedField(fields, 'investor.alphalend_position_cap.image_url'),
positionId: this.getNestedField(fields, 'investor.alphalend_position_cap.position_id'),
},
},
};
}, 'Failed to parse SlushSingleAssetLooping pool object');
Expand Down Expand Up @@ -285,6 +295,8 @@ export class SlushSingleAssetLoopingStrategy extends BaseStrategy<
const alphalendClient = new AlphalendClient('mainnet', this.context.blockchain.suiClient);
await alphalendClient.updatePrices(tx, [this.poolLabel.asset.type]);

await this.collectAndSwapRewards(tx);

// Get coin object
const depositCoin = await this.context.blockchain.getCoinObject(
tx,
Expand Down Expand Up @@ -335,6 +347,8 @@ export class SlushSingleAssetLoopingStrategy extends BaseStrategy<
const alphalendClient = new AlphalendClient('mainnet', this.context.blockchain.suiClient);
await alphalendClient.updatePrices(tx, [this.poolLabel.asset.type]);

await this.collectAndSwapRewards(tx);

let xTokenAmount = this.coinAmountToXToken(options.amount);
if (options.withdrawMax) {
xTokenAmount = this.receiptObjects[0].xTokens;
Expand All @@ -360,6 +374,7 @@ export class SlushSingleAssetLoopingStrategy extends BaseStrategy<
async claimWithdraw(tx: Transaction, withdrawRequestId: string, address: string) {
const alphalendClient = new AlphalendClient('mainnet', this.context.blockchain.suiClient);
await alphalendClient.updatePrices(tx, [this.poolLabel.asset.type]);
await this.collectAndSwapRewards(tx);
const positionCaps = await this.context.getSlushPositionCaps(address);
if (positionCaps.length === 0) {
throw new Error('No position cap found for claim');
Expand All @@ -384,6 +399,7 @@ export class SlushSingleAssetLoopingStrategy extends BaseStrategy<
async cancelWithdraw(tx: Transaction, withdrawRequestId: string, address: string) {
const alphalendClient = new AlphalendClient('mainnet', this.context.blockchain.suiClient);
await alphalendClient.updatePrices(tx, [this.poolLabel.asset.type]);
await this.collectAndSwapRewards(tx);
const positionCaps = await this.context.getSlushPositionCaps(address);
if (positionCaps.length === 0) {
throw new Error('No position cap found for cancellation');
Expand All @@ -407,6 +423,164 @@ export class SlushSingleAssetLoopingStrategy extends BaseStrategy<
async claimRewards(_tx: Transaction, _alphaReceipt: TransactionResult) {
return;
}

/**
* Collect rewards from the position and swap them to the base asset
* Follows the same pattern as SingleAssetLooping strategy
*/
private async collectAndSwapRewards(tx: Transaction) {
const [
alphaCoin,
stsuiCoin,
suiCoin,
blueCoin,
deepCoin,
usdcCoin,
walCoin,
tbtcCoin,
suibtcCoin,
xaumCoin,
] = await this.context.getCoinsBySymbols([
'ALPHA',
'stSUI',
'SUI',
'BLUE',
'DEEP',
'USDC',
'WAL',
'tBTC',
'wBTC',
'XAUm',
]);

const coinTypes = [
alphaCoin,
stsuiCoin,
suiCoin,
blueCoin,
deepCoin,
usdcCoin,
walCoin,
tbtcCoin,
suibtcCoin,
xaumCoin,
].map((entry) => entry.coinType);

const alphalendClient = new AlphalendClient(
this.context.blockchain.network,
this.context.blockchain.suiClient,
);

// Get position ID from the pool object (investor is embedded)
const positionId = this.poolObject.investor.positionCap.positionId;

let portfolio = await alphalendClient.getUserPortfolioFromPosition(positionId);
let rewards = portfolio?.rewardsToClaim;

if (!rewards) {
console.log('no rewards for pool id: ', this.poolLabel.poolId);
return;
}

// Loop through rewards and collect/swap each one
for (const x of rewards) {
if (x.coinType == alphaCoin.coinType) {
// ALPHA -> stSUI -> SUI
tx.moveCall({
target: `${this.poolLabel.packageId}::alphalend_slush_locked_loop_pool::collect_reward_and_swap_bluefin`,
typeArguments: [this.poolLabel.asset.type, alphaCoin.coinType, stsuiCoin.coinType],
arguments: [
tx.object(VERSIONS.SLUSH),
tx.object(this.poolLabel.poolId),
tx.object(ALPHALEND_LENDING_PROTOCOL_ID),
tx.object(
await this.context.getPoolIdBySymbolsAndProtocol('ALPHA', 'stSUI', 'bluefin'),
),
tx.object(GLOBAL_CONFIGS.BLUEFIN),
tx.pure.bool(true),
tx.pure.bool(false),
tx.pure.u64(1000),
tx.object(CLOCK_PACKAGE_ID),
],
});
tx.moveCall({
target: `${this.poolLabel.packageId}::alphalend_slush_locked_loop_pool::collect_reward_and_swap_bluefin`,
typeArguments: [this.poolLabel.asset.type, stsuiCoin.coinType, suiCoin.coinType],
arguments: [
tx.object(VERSIONS.SLUSH),
tx.object(this.poolLabel.poolId),
tx.object(ALPHALEND_LENDING_PROTOCOL_ID),
tx.object(await this.context.getPoolIdBySymbolsAndProtocol('stSUI', 'SUI', 'bluefin')),
tx.object(GLOBAL_CONFIGS.BLUEFIN),
tx.pure.bool(true),
tx.pure.bool(true),
tx.pure.u64(10),
tx.object(CLOCK_PACKAGE_ID),
],
});
} else if (coinTypes.includes(x.coinType) && x.coinType != this.poolLabel.asset.type) {
// Other rewards -> SUI
tx.moveCall({
target: `${this.poolLabel.packageId}::alphalend_slush_locked_loop_pool::collect_reward_and_swap_bluefin`,
typeArguments: [this.poolLabel.asset.type, x.coinType, suiCoin.coinType],
arguments: [
tx.object(VERSIONS.SLUSH),
tx.object(this.poolLabel.poolId),
tx.object(ALPHALEND_LENDING_PROTOCOL_ID),
tx.object(
await this.context.getPoolIdByTypesAndProtocol(
x.coinType,
suiCoin.coinType,
'bluefin',
),
),
tx.object(GLOBAL_CONFIGS.BLUEFIN),
tx.pure.bool(true),
tx.pure.bool(true),
tx.pure.u64(10000),
tx.object(CLOCK_PACKAGE_ID),
],
});
}
}

// After collecting all rewards, handle base asset conversion
// If pool asset is USDSUI, convert SUI -> USDC, then USDC -> USDSUI
if (this.poolLabel.asset.name === 'USDSUI') {
// SUI -> USDC
tx.moveCall({
target: `${this.poolLabel.packageId}::alphalend_slush_locked_loop_pool::collect_reward_and_swap_bluefin`,
typeArguments: [this.poolLabel.asset.type, suiCoin.coinType, usdcCoin.coinType],
arguments: [
tx.object(VERSIONS.SLUSH),
tx.object(this.poolLabel.poolId),
tx.object(ALPHALEND_LENDING_PROTOCOL_ID),
tx.object(await this.context.getPoolIdBySymbolsAndProtocol('SUI', 'USDC', 'bluefin')),
tx.object(GLOBAL_CONFIGS.BLUEFIN),
tx.pure.bool(true),
tx.pure.bool(true),
tx.pure.u64(10000),
tx.object(CLOCK_PACKAGE_ID),
],
});
// USDC -> USDSUI (base asset)
tx.moveCall({
target: `${this.poolLabel.packageId}::alphalend_slush_locked_loop_pool::collect_reward_and_swap_bluefin`,
typeArguments: [this.poolLabel.asset.type, this.poolLabel.asset.type, usdcCoin.coinType],
arguments: [
tx.object(VERSIONS.SLUSH),
tx.object(this.poolLabel.poolId),
tx.object(ALPHALEND_LENDING_PROTOCOL_ID),
tx.object(await this.context.getPoolIdBySymbolsAndProtocol('USDC', 'USDSUI', 'bluefin')),
tx.object(GLOBAL_CONFIGS.BLUEFIN),
tx.pure.bool(false),
tx.pure.bool(true),
tx.pure.u64(10),
tx.object(CLOCK_PACKAGE_ID),
],
});
}
}
}

/**
Expand All @@ -421,6 +595,12 @@ export interface SlushSingleAssetLoopingPoolObject {
investor: {
id: string;
marketId: string;
positionCap: {
clientAddress: string;
id: string;
imageUrl: string;
positionId: string;
};
};
}

Expand Down
13 changes: 6 additions & 7 deletions src/utils/testing-pools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const TEST_POOLS: PoolLabel[] = [
},
{
poolId: '0x0bca47c53d57d203d19611af98a4e723c52cbf1bc58312360bfb5dcba0286de9',
packageId: '0x3221f39ee2ebdb94bff679589d836c9b4d6879194af745edf47f37c9d2c2e7c7',
packageId: '0xcd3c28d6643fa2060d73d6e98bcc8049744b32804b017213a897e9466c5feb4d',
strategyType: 'SlushSingleAssetLooping',
parentProtocol: 'Alphalend',
asset: {
Expand All @@ -58,21 +58,20 @@ export const TEST_POOLS: PoolLabel[] = [
isNative: true,
},
{
poolId: '0xccc08b2e42a88002b4bd505e7e0b5bed17079d4cafc2ccbe82da0172d5291867',
packageId: '0xf48bef4f768cb5dbd2c8993a1036a032a61ab526e5d0a940775611e8e24c09ca',
packageNumber: 12,
strategyType: 'SlushLending',
poolId: '0x0e1399fe66eca3147766bb113ae7b52b31243874c9e4a64a48e6d8cb91aa3c04',
packageId: '0xcd3c28d6643fa2060d73d6e98bcc8049744b32804b017213a897e9466c5feb4d',
strategyType: 'SlushSingleAssetLooping',
parentProtocol: 'Alphalend',
asset: {
name: 'USDSUI',
type: '0x44f838219cf67b058f3b37907b655f226153c18e33dfcd0da559a844fea9b1c1::usdsui::USDSUI',
},
events: {
autocompoundEventType:
'0x41b1def47b6259cd7306e049d6500eabb1a984e25558b56eefa9b6c000a038c3::alphalend_slush_investor::AutoCompoundingEvent',
'0xad19911966b8ba9d6c5240da9389ccf347059385a0e90fd19f9cce644560a989::alphalend_slush_locked_loop_pool::XtokenRatioChangeEventV2',
},
isActive: true,
poolName: 'ALPHALEND-SLUSH-LENDING-USDSUI',
poolName: 'ALPHALEND-SLUSH-USDSUI-SINGLE-LOOP',
isNative: true,
},
];
Loading