From f7db320afe069a43f9b0aaa09a055e3525afedc9 Mon Sep 17 00:00:00 2001 From: doomsower <12031673+doomsower@users.noreply.github.com> Date: Wed, 10 Jul 2024 16:38:09 -0500 Subject: [PATCH] feat: claim degen nfts --- package.json | 3 +- src/log/index.ts | 3 + .../liquidate/LiquidationStrategyV3Partial.ts | 95 +++++++++++++++++++ src/services/liquidate/types.ts | 15 ++- 4 files changed, 114 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 67ccb00..b667cc3 100644 --- a/package.json +++ b/package.json @@ -64,5 +64,6 @@ "prettier --write" ], "*.{json,md}": "prettier --write" - } + }, + "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610" } diff --git a/src/log/index.ts b/src/log/index.ts index 37701b2..fd4a574 100644 --- a/src/log/index.ts +++ b/src/log/index.ts @@ -1,7 +1,10 @@ import type { IFactory } from "di-at-home"; +import { createRequire } from "module"; import type { DestinationStream, Logger as ILogger, LoggerOptions } from "pino"; import { pino } from "pino"; +const require = createRequire(import.meta.url); + import { DI } from "../di.js"; @DI.Factory(DI.Logger) diff --git a/src/services/liquidate/LiquidationStrategyV3Partial.ts b/src/services/liquidate/LiquidationStrategyV3Partial.ts index fc764e6..52088b5 100644 --- a/src/services/liquidate/LiquidationStrategyV3Partial.ts +++ b/src/services/liquidate/LiquidationStrategyV3Partial.ts @@ -24,6 +24,7 @@ import { iaclAbi, iCreditConfiguratorV3Abi, iCreditManagerV3Abi, + iDegenDistributorV3Abi, } from "@gearbox-protocol/types/abi"; import type { Address, SimulateContractReturnType } from "viem"; import { getContract, parseEther } from "viem"; @@ -38,6 +39,7 @@ import AbstractLiquidationStrategyV3 from "./AbstractLiquidationStrategyV3.js"; import type { ILiquidationStrategy, MakeLiquidatableResult, + MerkleDistributorInfo, PartialLiquidationPreview, } from "./types.js"; import type { IPriceHelperContract, TokenPriceInfo } from "./viem-types.js"; @@ -519,6 +521,13 @@ export default class LiquidationStrategyV3Partial this.logger.info(`set bot to ${bot} in tx ${receipt.transactionHash}`); } const cmToCa = await this.#getLiquidatorAccounts(cms); + + try { + await this.#claimDegenNFTs(cmToCa, cms); + } catch (e) { + this.logger.warn(`failed to obtain degen NFTs: ${e}`); + } + for (const cm of cms) { const { address, name } = cm; const ca = cmToCa[address]; @@ -549,6 +558,92 @@ export default class LiquidationStrategyV3Partial return Object.fromEntries(cms.map((cm, i) => [cm.address, results[i]])); } + /** + * Claim NFT tokens as liquidator contract, so that the contract can open credit accounts in Degen NFT protected credit managers + * @param cmToCa + * @param cms + * @returns + */ + async #claimDegenNFTs( + cmToCa: Record, + cms: CreditManagerData[], + ): Promise { + const account = this.partialLiquidator; + + let nfts = 0; + for (const { address, name, degenNFT } of cms) { + if (cmToCa[address] === ADDRESS_0X0 && degenNFT !== ADDRESS_0X0) { + this.logger.debug( + `need degen NFT ${degenNFT} for credit manager ${name}`, + ); + nfts++; + } + } + if (nfts === 0) { + return; + } + + const distributor = await this.addressProvider.findService( + "DEGEN_DISTRIBUTOR", + 0, + 0, + ); + this.logger.debug(`degen distributor: ${distributor}`); + const [distributorNFT, merkelRoot, claimed] = + await this.client.pub.multicall({ + allowFailure: false, + contracts: [ + { + address: distributor, + abi: iDegenDistributorV3Abi, + functionName: "degenNFT", + }, + { + address: distributor, + abi: iDegenDistributorV3Abi, + functionName: "merkleRoot", + }, + { + address: distributor, + abi: iDegenDistributorV3Abi, + functionName: "claimed", + args: [account], + }, + ], + }); + const merkleRootURL = `https://dm.gearbox.finance/${this.config.network.toLowerCase()}_${merkelRoot}.json`; + this.logger.debug( + `merkle root: ${merkleRootURL}, degen distributor NFT: ${distributorNFT}, claimed: ${claimed}`, + ); + + const resp = await fetch(merkleRootURL); + const merkle = (await resp.json()) as MerkleDistributorInfo; + const claims = merkle.claims[account]; + if (!claims) { + throw new Error(`${account} is not eligible for degen NFT claim`); + } + this.logger.debug(claims, `claims`); + if (BigInt(claims.amount) <= claimed) { + throw new Error(`already claimed`); + } + + const receipt = await this.client.simulateAndWrite({ + address: distributor, + abi: iDegenDistributorV3Abi, + functionName: "claim", + args: [ + BigInt(claims.index), // uint256 index, + account, // address account, + BigInt(claims.amount), // uint256 totalAmount, + claims.proof, // bytes32[] calldata merkleProof + ], + }); + if (receipt.status === "reverted") { + throw new Error(`degenDistributor.claim reverted`); + } + this.logger.debug(`${account} claimed ${BigInt(claims.amount)} degenNFTs`); + } + async #registerCM(cm: CreditManagerData): Promise { const { address, name } = cm; try { diff --git a/src/services/liquidate/types.ts b/src/services/liquidate/types.ts index 8993d56..7d15ef4 100644 --- a/src/services/liquidate/types.ts +++ b/src/services/liquidate/types.ts @@ -2,7 +2,7 @@ import type { OptimisticResultV2, PartialLiquidationCondition, } from "@gearbox-protocol/types/optimist"; -import type { Address, Hex, SimulateContractReturnType } from "viem"; +import type { Address, Hash, Hex, SimulateContractReturnType } from "viem"; import type { CreditAccountData, @@ -102,3 +102,16 @@ export interface MakeLiquidatableResult { snapshotId?: Hex; partialLiquidationCondition?: PartialLiquidationCondition; } + +export interface MerkleDistributorInfo { + merkleRoot: Hash; + tokenTotal: string; + claims: Record< + Address, + { + index: number; + amount: string; + proof: Hash[]; + } + >; +}