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
13 changes: 10 additions & 3 deletions src/filler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { stringify } from './utils/json.js';
import { logger } from './utils/logger.js';
import { SorobanHelper } from './utils/soroban_helper.js';

const MAX_WITHDRAW = BigInt('9223372036854775807');

/**
* Check if the filler supports bidding on the auction.
* @param filler - The filler to check
Expand Down Expand Up @@ -197,7 +199,7 @@ export function managePositions(
let withdrawAmount: bigint;
if (hasLeftoverLiabilities.length === 0) {
// no liabilities, withdraw the full position
withdrawAmount = BigInt('9223372036854775807');
withdrawAmount = MAX_WITHDRAW;
} else {
if (filler.minHealthFactor * 1.005 > effectiveCollateral / effectiveLiabilities) {
// stop withdrawing collateral if close to min health factor
Expand All @@ -208,20 +210,25 @@ export function managePositions(
(reserve.getCollateralFactor() * price);
const position = reserve.toAssetFromBTokenFloat(amount);
withdrawAmount =
maxWithdraw > position ? BigInt('9223372036854775807') : FixedMath.toFixed(maxWithdraw, 7);
maxWithdraw > position ? MAX_WITHDRAW : FixedMath.toFixed(maxWithdraw, 7);
}

// if this is not a full withdrawal, and the colleratal is not also a liability, stop
if (
!hasLeftoverLiabilities.includes(reserve.config.index) &&
withdrawAmount !== BigInt('9223372036854775807')
withdrawAmount !== MAX_WITHDRAW
) {
break;
}
// require the filler to keep at least the min collateral balance of their primary asset
if (reserve.assetId === filler.primaryAsset) {
const toMinPosition = reserve.toAssetFromBToken(amount) - filler.minPrimaryCollateral;
withdrawAmount = withdrawAmount > toMinPosition ? toMinPosition : withdrawAmount;
// if withdrawAmount is less than 1% of the minPrimaryCollateral stop
// this prevents dust withdraws from looping unwind events due to interest accrual
if (withdrawAmount < filler.minPrimaryCollateral / 100n) {
break;
}
}

if (withdrawAmount > 0n) {
Expand Down
46 changes: 46 additions & 0 deletions test/filler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,52 @@ describe('filler', () => {
expect(requests).toEqual(expectedRequests);
});

it('does not withdraw primary collateral if it only slightly above min collateral', () => {
const positions = new Positions(
// dTokens
new Map<number, bigint>([]),
// bTokens - (1.007 is ~b_rate)
new Map<number, bigint>([
[1, FixedMath.toFixed(100.8 / 1.007, 7)],
]),
new Map<number, bigint>([])
);
const balances = new Map<string, bigint>([
[assets[0], FixedMath.toFixed(575, 7)],
[assets[1], FixedMath.toFixed(3000, 7)],
[assets[2], FixedMath.toFixed(1000, 7)],
[assets[3], FixedMath.toFixed(0, 7)],
]);

const requests = managePositions(filler, mockPool, mockOracle, positions, balances);

const expectedRequests: Request[] = [];
expect(requests).toEqual(expectedRequests);
});

it('does nothing when only primary collateral and other non-repayable liability remain', () => {
const positions = new Positions(
// dTokens
new Map<number, bigint>([[0, FixedMath.toFixed(200, 7)]]),
// bTokens
new Map<number, bigint>([
[1, FixedMath.toFixed(150, 7)],
]),
new Map<number, bigint>([])
);
const balances = new Map<string, bigint>([
[assets[0], FixedMath.toFixed(0, 7)],
[assets[1], FixedMath.toFixed(3000, 7)],
[assets[2], FixedMath.toFixed(1000, 7)],
[assets[3], FixedMath.toFixed(0, 7)],
]);

const requests = managePositions(filler, mockPool, mockOracle, positions, balances);

const expectedRequests: Request[] = [];
expect(requests).toEqual(expectedRequests);
});

it('clears smallest collateral position first', () => {
const positions = new Positions(
// dTokens
Expand Down
Loading