Skip to content

Commit

Permalink
fix: implement price updates for batch liquidator
Browse files Browse the repository at this point in the history
  • Loading branch information
doomsower committed Aug 9, 2024
1 parent bd4b196 commit 2451947
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 26 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
"pino-pretty": "^11.2.2"
},
"devDependencies": {
"@aws-sdk/client-s3": "^3.626.0",
"@aws-sdk/client-s3": "^3.627.0",
"@commitlint/cli": "^19.4.0",
"@commitlint/config-conventional": "^19.2.2",
"@flashbots/ethers-provider-bundle": "^1.0.0",
"@gearbox-protocol/eslint-config": "2.0.0-next.2",
"@gearbox-protocol/liquidator-v2-contracts": "^2.1.0-next.18",
"@gearbox-protocol/liquidator-v2-contracts": "^2.1.0-next.19",
"@gearbox-protocol/prettier-config": "2.0.0-next.0",
"@gearbox-protocol/sdk-gov": "^2.14.1",
"@gearbox-protocol/types": "^1.11.0",
Expand All @@ -54,7 +54,7 @@
"redstone-protocol": "^1.0.5",
"tsx": "^4.17.0",
"typescript": "^5.5.4",
"viem": "^2.19.2",
"viem": "^2.19.3",
"vitest": "^2.0.5"
},
"prettier": "@gearbox-protocol/prettier-config",
Expand Down
84 changes: 75 additions & 9 deletions src/services/RedstoneServiceV3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ import type { PriceOnDemandExtras, PriceUpdate } from "./liquidate/index.js";
import type { RedstoneFeed } from "./OracleServiceV3.js";
import type OracleServiceV3 from "./OracleServiceV3.js";

interface RedstoneUpdate extends RedstoneFeed {
/**
* In case when Redstone feed is using ticker to updates, this will be the original token
* Otherwise they are the same
*/
originalToken: Address;
}

export type RedstonePriceFeed = Extract<
PriceFeedData,
{ type: PriceFeedType.REDSTONE_ORACLE }
Expand Down Expand Up @@ -113,12 +121,17 @@ export class RedstoneServiceV3 {
const redstoneFeeds = this.oracle.getRedstoneFeeds(activeOnly);
const tickers = tickerInfoTokensByNetwork[this.config.network];

const redstoneUpdates: RedstoneFeed[] = [];
const redstoneUpdates: RedstoneUpdate[] = [];
for (const t of tokens) {
const token = t.toLowerCase();
const token = t.toLowerCase() as Address;
const feeds = redstoneFeeds[token];
if (feeds?.length) {
redstoneUpdates.push(...feeds);
redstoneUpdates.push(
...feeds.map(f => ({
...f,
originalToken: token,
})),
);
continue;
}
const symb = tokenSymbolByAddress[token];
Expand All @@ -130,8 +143,9 @@ export class RedstoneServiceV3 {
`will update redstone ticker ${ticker.symbol} for ${symb}`,
);
redstoneUpdates.push({
dataFeedId: ticker.dataId,
originalToken: token,
token: ticker.address,
dataFeedId: ticker.dataId,
reserve: false, // tickers are always added as main feed
});
} else {
Expand All @@ -149,8 +163,9 @@ export class RedstoneServiceV3 {
`need to update ${redstoneUpdates.length} redstone feeds: ${printFeeds(redstoneUpdates)}`,
);
const result = await Promise.all(
redstoneUpdates.map(({ token, dataFeedId, reserve }) =>
redstoneUpdates.map(({ originalToken, token, dataFeedId, reserve }) =>
this.#getRedstonePayloadForManualUsage(
originalToken,
token,
reserve,
"redstone-primary-prod",
Expand Down Expand Up @@ -238,8 +253,58 @@ export class RedstoneServiceV3 {
}));
}

/**
* Gets updates from redstone for multiple accounts at once
* Reduces duplication, so that we don't make redstone request twice if two accounts share a token
*
* @param accounts
* @param activeOnly
* @returns
*/
public async batchLiquidationPreviewUpdates(
accounts: CreditAccountData[],
activeOnly = false,
): Promise<Record<Address, PriceUpdate[]>> {
const tokensByAccount: Record<Address, Set<Address>> = {};
const allTokens = new Set<Address>();
for (const ca of accounts) {
const accTokens = tokensByAccount[ca.addr] ?? new Set<Address>();
for (const { token, balance, isEnabled } of ca.allBalances) {
if (isEnabled && balance > 10n) {
accTokens.add(token);
allTokens.add(token);
}
}
tokensByAccount[ca.addr] = accTokens;
}

const priceUpdates = await this.updatesForTokens(
Array.from(allTokens),
activeOnly,
);

const result: Record<Address, PriceUpdate[]> = {};
for (const [accAddr, accTokens] of Object.entries(tokensByAccount)) {
const accUpdates: PriceUpdate[] = [];
// There can be 2 price feeds (main and reserve) per originalToken
for (const u of priceUpdates) {
if (accTokens.has(u.originalToken)) {
accUpdates.push({
token: u.token,
reserve: u.reserve,
data: u.callData,
});
}
}
result[accAddr as Address] = accUpdates;
}

return result;
}

async #getRedstonePayloadForManualUsage(
token: Address,
originalToken: Address,
tokenOrTicker: Address,
reserve: boolean,
dataServiceId: string,
dataFeedId: string,
Expand All @@ -249,7 +314,7 @@ export class RedstoneServiceV3 {
const logger = this.logger.child(logContext);
const cacheAllowed = this.config.optimistic;
const key = redstoneCacheKey(
token,
tokenOrTicker,
reserve,
dataServiceId,
dataFeedId,
Expand Down Expand Up @@ -305,8 +370,9 @@ export class RedstoneServiceV3 {
] as const;
});

const response = {
token,
const response: PriceOnDemandExtras = {
originalToken,
token: tokenOrTicker,
reserve,
callData: result[0][0],
ts: result[0][1],
Expand Down
12 changes: 10 additions & 2 deletions src/services/liquidate/BatchLiquidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ export default class BatchLiquidator
index: number,
total: number,
): Promise<BatchLiquidationOutput> {
const priceUpdatesByAccount =
await this.redstone.batchLiquidationPreviewUpdates(accounts);
const inputs: EstimateBatchInput[] = [];
for (const ca of accounts) {
const cm = cms.find(m => ca.creditManager === m.address);
Expand All @@ -113,9 +115,14 @@ export default class BatchLiquidator
`cannot find credit manager data for ${ca.creditManager}`,
);
}
inputs.push(
this.pathFinder.getEstimateBatchInput(ca, cm, this.config.slippage),
// pathfinder returns input without price updates
const input = this.pathFinder.getEstimateBatchInput(
ca,
cm,
this.config.slippage,
);
input.priceUpdates = priceUpdatesByAccount[ca.addr];
inputs.push(input);
}
const { result } = await this.client.pub.simulateContract({
account: this.client.account,
Expand Down Expand Up @@ -219,6 +226,7 @@ export default class BatchLiquidator
pathAmount: "0", // TODO: ??
liquidatorPremium: (batch[a.addr]?.profit ?? 0n).toString(10),
liquidatorProfit: "0", // cannot compute for single account
priceUpdates: priceUpdatesByAccount[a.addr],
isError: !liquidated.has(a.addr),
error: getError(a),
batchId: `${index + 1}/${total}`,
Expand Down
5 changes: 5 additions & 0 deletions src/services/liquidate/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import type {
} from "../../data/index.js";

export interface PriceOnDemandExtras extends PriceOnDemand {
/**
* In case when token in PriceOnDemand is ticker, this will be the original token
* Otherwise they are the same
*/
originalToken: Address;
ts: number;
reserve: boolean;
}
Expand Down
1 change: 1 addition & 0 deletions src/utils/ethers-6-temp/pathfinder/pathfinder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class PathFinder {
pathOptions: pathOptions[0] ?? [], // TODO: what to put here?
iterations: BigInt(LOOPS_PER_TX),
force: false,
priceUpdates: [],
};
}

Expand Down
24 changes: 12 additions & 12 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@
"@smithy/util-utf8" "^2.0.0"
tslib "^2.6.2"

"@aws-sdk/client-s3@^3.626.0":
version "3.626.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.626.0.tgz#02556328c2c05ae11a0ca9f0e5fba90ac6c80e74"
integrity sha512-+ul1NEdiAuq5L0lhxWb+FcQuw+1RKU4lNugdX/EF3Lr6Bpuo384K/4r9cRwOo/6PqRYMIengBMc9Q2HVAu8ZWg==
"@aws-sdk/client-s3@^3.627.0":
version "3.627.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.627.0.tgz#9a5fe33b15fa5613085f7f254faecc0cc150ad58"
integrity sha512-XTbtRLPVfq2lHo0SUP6HJb6HgBsKsJR54bhhVTwj5SZ4G26KOmx2iFOz9SgHie5apU7vWIhijb48LIhbLArgGg==
dependencies:
"@aws-crypto/sha1-browser" "5.2.0"
"@aws-crypto/sha256-browser" "5.2.0"
Expand Down Expand Up @@ -1455,10 +1455,10 @@
eslint-plugin-simple-import-sort "^10.0.0"
eslint-plugin-unused-imports "^3.0.0"

"@gearbox-protocol/liquidator-v2-contracts@^2.1.0-next.18":
version "2.1.0-next.18"
resolved "https://registry.yarnpkg.com/@gearbox-protocol/liquidator-v2-contracts/-/liquidator-v2-contracts-2.1.0-next.18.tgz#701f57be55bac66ffea9306b7d572d77b2158d0c"
integrity sha512-OONhWYI5M+EEjVyvDlYqKcQqOCGF4uLj0fJYLCG3zA20mwOidnY8BBQPOof4D3UnPqiumBZ1hGXSRqyfQaqxhw==
"@gearbox-protocol/liquidator-v2-contracts@^2.1.0-next.19":
version "2.1.0-next.19"
resolved "https://registry.yarnpkg.com/@gearbox-protocol/liquidator-v2-contracts/-/liquidator-v2-contracts-2.1.0-next.19.tgz#858912a5e3ebf6f09dd563d3966f11a25daf70ba"
integrity sha512-5B1M1olNd5Dz2kobtCgw2+JSkMuknyMZ4tfm9H4VLDAoWDq+u8gJicfoAsgQ3q4FRb63vrZIEsjOY+h/VBDAlQ==

"@gearbox-protocol/prettier-config@2.0.0-next.0":
version "2.0.0-next.0"
Expand Down Expand Up @@ -5816,10 +5816,10 @@ uuid@^9.0.1:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==

viem@^2.19.2:
version "2.19.2"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.19.2.tgz#11f03621fd0d0d742f04e3da30fa49093a3cf612"
integrity sha512-BrR7fEEpuu9Om7obQGThb4BEu00PPHPKaUx+snB/F6yBZtr34FdXCPnphr+S73W2iIu/mt3yaRkfkLlD6a1R5g==
viem@^2.19.3:
version "2.19.3"
resolved "https://registry.yarnpkg.com/viem/-/viem-2.19.3.tgz#537773f50d3a0e7436d2465898afc62c0c25c01c"
integrity sha512-djOw1X/jOvDOEMiol4g/T030MVncF2utC9G929ODNJ/00E7UXjSpwOeuyapmaqn831eSIHlELicZETYl2vI9oQ==
dependencies:
"@adraffy/ens-normalize" "1.10.0"
"@noble/curves" "1.4.0"
Expand Down

0 comments on commit 2451947

Please sign in to comment.