Skip to content

Commit

Permalink
Fix fixedSetAmount issuance method and add related tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ckoopmann committed Apr 12, 2024
1 parent 196f409 commit 93de35d
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 2 deletions.
2 changes: 2 additions & 0 deletions contracts/exchangeIssuance/FlashMintLeveraged.sol
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ contract FlashMintLeveraged is ReentrancyGuard, IFlashLoanRecipient{
DEXAdapter.SwapData memory _swapDataInputToken
)
external
virtual
nonReentrant
{
_initiateIssuance(
Expand Down Expand Up @@ -374,6 +375,7 @@ contract FlashMintLeveraged is ReentrancyGuard, IFlashLoanRecipient{
DEXAdapter.SwapData memory _swapDataInputToken
)
external
virtual
payable
nonReentrant
{
Expand Down
71 changes: 71 additions & 0 deletions contracts/exchangeIssuance/FlashMintLeveragedExtended.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { ISetToken } from "../interfaces/ISetToken.sol";
import { IWETH } from "../interfaces/IWETH.sol";



/**
* @title FlashMintLeveragedExtended
* @author Index Coop
Expand Down Expand Up @@ -61,6 +62,76 @@ contract FlashMintLeveragedExtended is FlashMintLeveraged {
{
}

/**
* Trigger issuance of set token paying with any arbitrary ERC20 token
*
* @param _setToken Set token to issue
* @param _setAmount Amount to issue
* @param _inputToken Input token to pay with
* @param _maxAmountInputToken Maximum amount of input token to spend
* @param _swapDataDebtForCollateral Data (token addresses and fee levels) to describe the swap path from Debt to collateral token
* @param _swapDataInputToken Data (token addresses and fee levels) to describe the swap path from input to collateral token
*/
function issueExactSetFromERC20(
ISetToken _setToken,
uint256 _setAmount,
address _inputToken,
uint256 _maxAmountInputToken,
DEXAdapter.SwapData memory _swapDataDebtForCollateral,
DEXAdapter.SwapData memory _swapDataInputToken
)
external
override
nonReentrant
{
uint256 inputTokenBalanceBefore = IERC20(_inputToken).balanceOf(address(this));
IERC20(_inputToken).transferFrom(msg.sender, address(this), _maxAmountInputToken);
_initiateIssuance(
_setToken,
_setAmount,
_inputToken,
_maxAmountInputToken,
_swapDataDebtForCollateral,
_swapDataInputToken
);
uint256 amountToReturn = IERC20(_inputToken).balanceOf(address(this)).sub(inputTokenBalanceBefore);
IERC20(_inputToken).transfer(msg.sender, amountToReturn);
}

/**
* Trigger issuance of set token paying with Eth
*
* @param _setToken Set token to issue
* @param _setAmount Amount to issue
* @param _swapDataDebtForCollateral Data (token addresses and fee levels) to describe the swap path from Debt to collateral token
* @param _swapDataInputToken Data (token addresses and fee levels) to describe the swap path from eth to collateral token
*/
function issueExactSetFromETH(
ISetToken _setToken,
uint256 _setAmount,
DEXAdapter.SwapData memory _swapDataDebtForCollateral,
DEXAdapter.SwapData memory _swapDataInputToken
)
external
override
payable
nonReentrant
{
uint256 inputTokenBalanceBefore = IERC20(addresses.weth).balanceOf(address(this));
IWETH(addresses.weth).deposit{value: msg.value}();
_initiateIssuance(
_setToken,
_setAmount,
DEXAdapter.ETH_ADDRESS,
msg.value,
_swapDataDebtForCollateral,
_swapDataInputToken
);
uint256 amountToReturn = IERC20(addresses.weth).balanceOf(address(this)).sub(inputTokenBalanceBefore);
IWETH(addresses.weth).withdraw(amountToReturn);
msg.sender.transfer(amountToReturn);
}

function issueSetFromExactERC20(
ISetToken _setToken,
uint256 _minSetAmount,
Expand Down
128 changes: 126 additions & 2 deletions test/integration/ethereum/flashMintLeveragedExtended.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import "module-alias/register";
import { Account, Address } from "@utils/types";
import DeployHelper from "@utils/deploys";
import { getAccounts, getWaffleExpect } from "@utils/index";
import { getAccounts, getWaffleExpect, preciseMul } from "@utils/index";
import { setBlockNumber } from "@utils/test/testingUtils";
import { ethers } from "hardhat";
import { BigNumber, utils } from "ethers";
Expand Down Expand Up @@ -214,19 +214,143 @@ if (process.env.INTEGRATIONTEST) {

["collateralToken", "WETH", "ETH"].forEach(inputTokenName => {
describe(`When input/output token is ${inputTokenName}`, () => {
let subjectMinSetAmount: BigNumber;
let amountIn: BigNumber;
beforeEach(async () => {
amountIn = ether(2);
});

describe(
inputTokenName === "ETH" ? "issueExactSetFromETH" : "#issueExactSetFromERC20",
() => {
let subjectSetAmount: BigNumber;
let swapDataDebtToCollateral: SwapData;
let swapDataInputToken: SwapData;

let inputToken: StandardTokenMock | IWETH;

let subjectSetToken: Address;
let subjectMaxAmountIn: BigNumber;
let subjectInputToken: Address;

beforeEach(async () => {
subjectSetAmount = ether(1);
swapDataDebtToCollateral = {
path: [addresses.tokens.weth, addresses.tokens.rETH],
fees: [500],
pool: ADDRESS_ZERO,
exchange: Exchange.UniV3,
};

swapDataInputToken = {
path: [],
fees: [],
pool: ADDRESS_ZERO,
exchange: Exchange.None,
};

if (inputTokenName === "collateralToken") {
inputToken = rEth;
} else {
swapDataInputToken = swapDataDebtToCollateral;

if (inputTokenName === "WETH") {
inputToken = weth;
await weth.deposit({ value: amountIn });
}
}

let inputTokenBalance: BigNumber;
if (inputTokenName === "ETH") {
subjectMaxAmountIn = amountIn;
} else {
inputTokenBalance = await inputToken.balanceOf(owner.address);
subjectMaxAmountIn = inputTokenBalance;
await inputToken.approve(flashMintLeveraged.address, subjectMaxAmountIn);
subjectInputToken = inputToken.address;
}
subjectSetToken = setToken.address;
});

async function subject() {
if (inputTokenName === "ETH") {
return flashMintLeveraged.issueExactSetFromETH(
subjectSetToken,
subjectSetAmount,
swapDataDebtToCollateral,
swapDataInputToken,
{ value: subjectMaxAmountIn },
);
}
return flashMintLeveraged.issueExactSetFromERC20(
subjectSetToken,
subjectSetAmount,
subjectInputToken,
subjectMaxAmountIn,
swapDataDebtToCollateral,
swapDataInputToken,
);
}

async function subjectQuote() {
return flashMintLeveraged.callStatic.getIssueExactSet(
subjectSetToken,
subjectSetAmount,
swapDataDebtToCollateral,
swapDataInputToken,
);
}

it("should issue the correct amount of tokens", async () => {
const setBalancebefore = await setToken.balanceOf(owner.address);
await subject();
const setBalanceAfter = await setToken.balanceOf(owner.address);
const setObtained = setBalanceAfter.sub(setBalancebefore);
expect(setObtained).to.eq(subjectSetAmount);
});

it("should spend less than specified max amount", async () => {
const inputBalanceBefore =
inputTokenName === "ETH"
? await owner.wallet.getBalance()
: await inputToken.balanceOf(owner.address);
await subject();
const inputBalanceAfter =
inputTokenName === "ETH"
? await owner.wallet.getBalance()
: await inputToken.balanceOf(owner.address);
const inputSpent = inputBalanceBefore.sub(inputBalanceAfter);
expect(inputSpent.gt(0)).to.be.true;
expect(inputSpent.lte(subjectMaxAmountIn)).to.be.true;
});

it("should quote the correct input amount", async () => {
const inputBalanceBefore =
inputTokenName === "ETH"
? await owner.wallet.getBalance()
: await inputToken.balanceOf(owner.address);
await subject();
const inputBalanceAfter =
inputTokenName === "ETH"
? await owner.wallet.getBalance()
: await inputToken.balanceOf(owner.address);
const inputSpent = inputBalanceBefore.sub(inputBalanceAfter);

const quotedInputAmount = await subjectQuote();

expect(quotedInputAmount).to.gt(preciseMul(inputSpent, ether(0.99)));
expect(quotedInputAmount).to.lt(preciseMul(inputSpent, ether(1.01)));
});
},
);

describe(
inputTokenName === "ETH" ? "issueSetFromExactETH" : "#issueSetFromExactERC20",
() => {
let swapDataDebtToCollateral: SwapData;
let swapDataInputToken: SwapData;

let inputToken: StandardTokenMock | IWETH;
let subjectMinSetAmount: BigNumber;

let subjectSetToken: Address;
let subjectAmountIn: BigNumber;
Expand Down

0 comments on commit 93de35d

Please sign in to comment.