Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VEN-2238]: add move debt operator #2

Closed
wants to merge 11 commits into from
Prev Previous commit
Next Next commit
feat: support batch repayments
  • Loading branch information
kkirka committed Dec 22, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 8ecc567b900c0ef7de8ecff6140d0cab76c5d8e2
54 changes: 46 additions & 8 deletions contracts/operators/MoveDebtOperator.sol
Original file line number Diff line number Diff line change
@@ -18,8 +18,9 @@ contract MoveDebtOperator is ExactOutputFlashSwap {
struct MoveDebtParams {
uint256 maxExtraAmount;
address originalSender;
address originalBorrower;
uint256 repayAmount;
address[] originalBorrowers;
uint256[] repayAmounts;
uint256 totalRepayAmount;
IVBep20 vTokenToBorrow;
}

@@ -37,16 +38,50 @@ contract MoveDebtOperator is ExactOutputFlashSwap {
uint256 maxExtraAmount,
bytes memory path
) external {
address[] memory originalBorrowers = new address[](1);
uint256[] memory repayAmounts = new uint256[](1);
originalBorrowers[0] = originalBorrower;
repayAmounts[0] = repayAmount;
_moveDebts(originalBorrowers, repayAmounts, repayAmount, vTokenToBorrow, maxExtraAmount, path);
}

function moveAllDebts(
address[] memory originalBorrowers,
IVBep20 vTokenToBorrow,
uint256 maxExtraAmount,
bytes memory path
) external {
uint256 borrowersCount = originalBorrowers.length;
IVBep20 vTokenToRepay = DELEGATE.vTokenToRepay();

uint256[] memory repayAmounts = new uint256[](borrowersCount);
uint256 totalRepayAmount = 0;
for (uint256 i = 0; i < borrowersCount; ++i) {
uint256 amount = vTokenToRepay.borrowBalanceCurrent(originalBorrowers[i]);
repayAmounts[i] = amount;
totalRepayAmount += amount;
}
_moveDebts(originalBorrowers, repayAmounts, totalRepayAmount, vTokenToBorrow, maxExtraAmount, path);
}

function _moveDebts(
address[] memory originalBorrowers,
uint256[] memory repayAmounts,
uint256 totalRepayAmount,
IVBep20 vTokenToBorrow,
uint256 maxExtraAmount,
bytes memory path
) internal {
MoveDebtParams memory params = MoveDebtParams({
maxExtraAmount: maxExtraAmount,
originalSender: msg.sender,
originalBorrower: originalBorrower,
repayAmount: repayAmount,
originalBorrowers: originalBorrowers,
repayAmounts: repayAmounts,
totalRepayAmount: totalRepayAmount,
vTokenToBorrow: vTokenToBorrow
});

bytes memory data = abi.encode(params);
_flashSwap(FlashSwapParams({ amountOut: repayAmount, path: path, data: data }));
_flashSwap(FlashSwapParams({ amountOut: totalRepayAmount, path: path, data: data }));
}

function _onMoneyReceived(bytes memory data) internal override returns (IERC20 tokenIn, uint256 maxAmountIn) {
@@ -56,8 +91,11 @@ contract MoveDebtOperator is ExactOutputFlashSwap {

uint256 balanceBefore = borrowToken.balanceOf(address(this));

approveOrRevert(repayToken, address(DELEGATE), params.repayAmount);
DELEGATE.moveDebt(params.originalBorrower, params.repayAmount, params.vTokenToBorrow);
approveOrRevert(repayToken, address(DELEGATE), params.totalRepayAmount);
uint256 borrowersCount = params.originalBorrowers.length;
for (uint256 i = 0; i < borrowersCount; ++i) {
DELEGATE.moveDebt(params.originalBorrowers[i], params.repayAmounts[i], params.vTokenToBorrow);
}
approveOrRevert(repayToken, address(DELEGATE), 0);

if (params.maxExtraAmount > 0) {
71 changes: 70 additions & 1 deletion tests/fork/MoveDebtOperator.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
import { loadFixture } from "@nomicfoundation/hardhat-network-helpers";
import { expect } from "chai";
import { parseEther, parseUnits } from "ethers/lib/utils";
import { formatUnits, parseEther, parseUnits } from "ethers/lib/utils";
import { ethers } from "hardhat";
import { SignerWithAddress } from "hardhat-deploy-ethers/signers";

import {
IERC20,
IERC20__factory,
IVBep20,
MoveDebtDelegate,
MoveDebtOperator,
MoveDebtOperator__factory,
@@ -68,13 +70,15 @@ forking({ bscmainnet: 34341800 } as const, addresses => {
let admin: SignerWithAddress;
let busd: IERC20;
let usdt: IERC20;
let vBUSD: IVBep20;
let moveDebtOperator: MoveDebtOperator;
let moveDebtDelegate: MoveDebtDelegate;

beforeEach(async () => {
[admin] = await ethers.getSigners();
({ moveDebtOperator, busd, usdt } = await loadFixture(moveDebtOperatorFixture));
moveDebtDelegate = await ethers.getContractAt("MoveDebtDelegate", addresses.MoveDebtDelegate);
vBUSD = await ethers.getContractAt("IVBep20", addresses.vBUSD);
});

it("should work with a single-hop path from BUSD to USDT", async () => {
@@ -98,6 +102,71 @@ forking({ bscmainnet: 34341800 } as const, addresses => {
await expect(tx).to.emit(busd, "Transfer").withArgs(moveDebtDelegate.address, addresses.vBUSD, repayAmount);
});

it("should repay all debts of a list of borrowers", async () => {
const path = ethers.utils.hexlify(ethers.utils.concat([addresses.BUSD, "0x000064", addresses.USDT]));
const borrowers = [
"0x6b69d62616633d71d36cf253021ae717db2e09c7",
"0xc870b1b5a7787ef5877190fe10851fdceb402d47",
"0xca925c8900f6f27e89b9bebc4987a655cb43911f",
"0xdc9af4213dee2321c0461a59e761f76e4677fdb9",
"0xdf99f63bc8f1ce2fd7aa0c0140a0340f91fb4680",
"0xac0c96c50bb4080bcb920a7c84dc23f0decddbd6",
"0xd3a5eb04d919d17b846d81b9875c652720b7be97",
"0xd53f6bae74603aaf74145e8150d11dbdb6fc805d",
"0xc4050213225baf4e2e9fb53f877caa727f05faf5",
"0x60f2563424db41fbfe308f46034f155f1c1e5d29",
"0x1e803cf10460bcd7235a87527105d1e2a3c6319b",
"0xf51c2e6a7e838923f30367b3d2f6d22db85a83b5",
"0x37e1e4b215c2a1d026444899b90f0bf29ef12576",
"0x3b052f0a3cfe703b63ac6fe688bbf16a67ecf738",
"0xcbc090c20d32b4415edacfc70665d4101cd940f6",
"0xccb5c516c5c3dddf1e46db9b3c399b0f5e542251",
"0xe9caddbc9f620098171c67385f5237b829a55db6",
"0x981e41c791f9cf5b5b0470df0637b95a5b4e00a2",
"0x8915cf710645794dad5eaee96b3e56675bf62651",
"0x0794e8235fbdfaf2061a6721ade61126d8493b35",
"0x7d6936e3dbeb9ff00e9d77ecc40eb59bf96c82b3",
"0xe7404e8c8c5607aea3ddfc6820c143bd21e750a1",
"0x82ec5d22cff969aa01afa7d082891b2cf5714769",
"0xabb3ed61b1928dc0a57839d6c8a38446762bdbfe",
"0x0e8c66bbfec32aa3fab86bba641f4f20fe457ca9",
"0xc7d785ffb0b5f92beb21382d44539048a5b5df62",
"0xd4e982b86590428588934fbd5510a61342ef7214",
"0x7261c1a6a47e596dfcf0e2f21140217acc5eded8",
"0xe33fca60a281431d82ac9ad55e37be9b587ead63",
"0xb75a7d3049654242617015dcedf2c49e1ca7ea65",
"0x88511937ddb0c65cf2b925200e185f590a43a267",
"0xdf38cd49bb821a3984277a252b4ecdda4ab7631c",
"0x614146018042d47dcde01a9400a8d14343047b67",
"0xd071f60ea1e2d4855cf34a6022372c33d046e34b",
"0x6dc3353dc6ba9d0cdc97af9d7eb5a4475ded4a3f",
"0x53d4bc5ccb0fa8ce8b853eb90086df93d1217026",
"0x58ceb7d9abbe3039e4aec356446f7c59aadae376",
"0x9edb330cac62e21f616d113754c7f7451179b5b2",
"0x22d1eca04a0ab99ebb57b9feeed45dee3df4e444",
"0x1956b3aae9c3e583584143d566612e7e721de141",
"0x0db95671310f0ba4cc0f29106593f8090037027a",
"0x00e1dfbb710d0d854f0d6736170b874b3690d0d7",
"0x4d390a71b61bdbee803960a00961de839d7c09d0",
"0xa139f8b283199b61efbe9c934c3a74656ed82618",
"0x8764c54e16304a26cd9356635431ce9a709d634c",
"0x6bc65848bfa839005c65d4d49989fcca9925f4ce",
"0x19132c5d648e0706bda8afa9e8ef3c57e86f50a9",
"0xe2a6d1c95672211a7d86fe394cc7eb1969e6258d",
"0x604c235ec0ab0b303090449a70550bab044e6be3",
"0xce1f6f19faec15e0806c8ad6378a3e9a7f74994e",
].map(ethers.utils.getAddress);
await usdt.connect(admin).approve(moveDebtOperator.address, parseUnits("100", 18));
const maxUsdtToSpend = parseUnits("30", 18);
const tx = await moveDebtOperator.connect(admin).moveAllDebts(borrowers, addresses.vUSDT, maxUsdtToSpend, path);
for (const borrower of borrowers) {
await expect(tx)
.to.emit(moveDebtDelegate, "DebtMoved")
.withArgs(borrower, addresses.vBUSD, anyValue, BNB_EXPLOITER, addresses.vUSDT, anyValue);
expect(await vBUSD.callStatic.borrowBalanceCurrent(borrower)).to.equal(0);
}
});

it("should work with a multi-hop path from BUSD to USDT", async () => {
const path = ethers.utils.hexlify(
ethers.utils.concat([addresses.BUSD, "0x0001f4", addresses.WBNB, "0x0001f4", addresses.USDT]),