From 936d366c3c7981b19fdf37e5997a6bd976a9e462 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sat, 10 Feb 2024 01:22:41 +0400 Subject: [PATCH 1/7] feat: setup fluid on mainnet --- contracts/mainnet/connectors/fluid/events.sol | 20 ++ .../mainnet/connectors/fluid/interface.sol | 49 ++++ contracts/mainnet/connectors/fluid/main.sol | 256 ++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 contracts/mainnet/connectors/fluid/events.sol create mode 100644 contracts/mainnet/connectors/fluid/interface.sol create mode 100644 contracts/mainnet/connectors/fluid/main.sol diff --git a/contracts/mainnet/connectors/fluid/events.sol b/contracts/mainnet/connectors/fluid/events.sol new file mode 100644 index 0000000..c0055ef --- /dev/null +++ b/contracts/mainnet/connectors/fluid/events.sol @@ -0,0 +1,20 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +contract Events { + event LogOperate( + address vaultAddress, + uint256 nftId, + int256 newCol, + int256 newDebt + ); + + event LogOperateWithIds( + address vaultAddress, + uint256 nftId, + int256 newCol, + int256 newDebt, + uint256[] getIds, + uint256[] setIds + ); +} diff --git a/contracts/mainnet/connectors/fluid/interface.sol b/contracts/mainnet/connectors/fluid/interface.sol new file mode 100644 index 0000000..a7eef73 --- /dev/null +++ b/contracts/mainnet/connectors/fluid/interface.sol @@ -0,0 +1,49 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +interface IVault { + /// @dev Single function which handles supply, withdraw, borrow & payback + /// @param nftId_ NFT ID for interaction. If 0 then create new NFT/position. + /// @param newCol_ new collateral. If positive then deposit, if negative then withdraw, if 0 then do nohing + /// @param newDebt_ new debt. If positive then borrow, if negative then payback, if 0 then do nohing + /// @param to_ address where withdraw or borrow should go. If address(0) then msg.sender + /// @return nftId_ if 0 then this returns the newly created NFT Id else returns the same NFT ID + /// @return final supply amount. Mainly if max withdraw using type(int).min then this is useful to get perfect amount else remain same as newCol_ + /// @return final borrow amount. Mainly if max payback using type(int).min then this is useful to get perfect amount else remain same as newDebt_ + function operate( + uint256 nftId_, // if 0 then new position + int256 newCol_, // if negative then withdraw + int256 newDebt_, // if negative then payback + address to_ // address at which the borrow & withdraw amount should go to. If address(0) then it'll go to msg.sender + ) + external + payable + returns ( + uint256, // nftId_ + int256, // final supply amount if - then withdraw + int256 // final borrow amount if - then payback + ); + + struct ConstantViews { + address liquidity; + address factory; + address adminImplementation; + address secondaryImplementation; + address supplyToken; + address borrowToken; + uint8 supplyDecimals; + uint8 borrowDecimals; + uint vaultId; + bytes32 liquidityTotalSupplySlot; + bytes32 liquidityTotalBorrowSlot; + bytes32 liquiditySupplyExchangePriceSlot; + bytes32 liquidityBorrowExchangePriceSlot; + bytes32 liquidityUserSupplySlot; + bytes32 liquidityUserBorrowSlot; + } + + function constantsView() + external + view + returns (ConstantViews memory constantsView_); +} diff --git a/contracts/mainnet/connectors/fluid/main.sol b/contracts/mainnet/connectors/fluid/main.sol new file mode 100644 index 0000000..9bb4c55 --- /dev/null +++ b/contracts/mainnet/connectors/fluid/main.sol @@ -0,0 +1,256 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +/** + * @title Fluid. + * @dev Lending & Borrowing. + */ + +import {Stores} from "../../common/stores.sol"; +import {TokenInterface} from "../../common/interfaces.sol"; +import {Events} from "./events.sol"; +import {IVault} from "./interface.sol"; + +abstract contract FluidConnector is Events, Stores { + /** + * @dev Returns Eth address + */ + function getEthAddr() internal pure returns (address) { + return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + } + + /** + * @dev Deposit, borrow, payback and withdraw asset from the vault. + * @notice Single function which handles supply, withdraw, borrow & payback + * @param vaultAddress_ Vault address. + * @param nftId_ NFT ID for interaction. If 0 then create new NFT/position. + * @param newCol_ New collateral. If positive then deposit, if negative then withdraw, if 0 then do nothing. + * For max deposit use type(uint25).max, for max withdraw use type(uint25).min. + * @param newDebt_ New debt. If positive then borrow, if negative then payback, if 0 then do nothing + * For max payback use type(uint25).min. + * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. + * Should always be positive. + * @param getIds_ Array of 5 elements to retrieve IDs: + * Nft Id, Supply amount, Withdraw amount, Borrow Amount, Payback Amount + * @param setIds_ Array of 5 elements to store IDs generated: + * Nft Id, Supply amount, Withdraw amount, Borrow Amount, Payback Amount + */ + function operate( + address vaultAddress_, + uint256 nftId_, + int256 newCol_, + int256 newDebt_, + uint256 repayApproveAmt_, + uint256[] memory getIds_, + uint256[] memory setIds_ + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + if (getIds_[1] > 0 && getIds_[2] > 0) { + revert("Supply and withdraw get IDs cannot both be > 0."); + } + + if (getIds_[3] > 0 && getIds_[4] > 0) { + revert("Borrow and payback get IDs cannot both be > 0."); + } + + if (setIds_[1] > 0 && setIds_[2] > 0) { + revert("Supply and withdraw set IDs cannot both be > 0."); + } + + if (setIds_[3] > 0 && setIds_[4] > 0) { + revert("Borrow and payback set IDs cannot both be > 0."); + } + + nftId_ = getUint(getIds_[0], nftId_); + + newCol_ = getIds_[1] > 0 + ? int256(getUint(getIds_[1], uint256(newCol_))) + : getIds_[2] > 0 + ? -int256(getUint(getIds_[2], uint256(newCol_))) + : newCol_; + + newDebt_ = getIds_[3] > 0 + ? int256(getUint(getIds_[3], uint256(newDebt_))) + : getIds_[4] > 0 + ? -int256(getUint(getIds_[4], uint256(newDebt_))) + : newDebt_; + + IVault vault_ = IVault(vaultAddress_); + + IVault.ConstantViews memory vaultDetails_ = vault_.constantsView(); + + uint256 ethAmount_; + + bool isColMax_ = newCol_ == type(int256).max; + + // Deposit + if (newCol_ > 0) { + if (vaultDetails_.supplyToken == getEthAddr()) { + ethAmount_ = isColMax_ + ? address(this).balance + : uint256(newCol_); + } else { + if (isColMax_) { + newCol_ = int256( + TokenInterface(vaultDetails_.supplyToken).balanceOf( + address(this) + ) + ); + } + + TokenInterface(vaultDetails_.supplyToken).approve( + vaultAddress_, + uint256(newCol_) + ); + } + } + + bool isPaybackMax_ = newDebt_ == type(int256).min; + + // Payback + if (newDebt_ < 0) { + if (vaultDetails_.borrowToken == getEthAddr()) { + // Needs to be positive as it will be send in msg.value + ethAmount_ = isPaybackMax_ + ? repayApproveAmt_ + : uint256(-1 * newDebt_); + } else { + isPaybackMax_ + ? TokenInterface(vaultDetails_.borrowToken).approve( + vaultAddress_, + repayApproveAmt_ + ) + : TokenInterface(vaultDetails_.borrowToken).approve( + vaultAddress_, + uint256(-1 * newDebt_) + ); + } + } + + // Note max withdraw will be handled by Fluid contract + (nftId_, newCol_, newDebt_) = vault_.operate{value: ethAmount_}( + nftId_, + newCol_, + newDebt_, + address(this) + ); + + setUint(setIds_[0], nftId_); + + setIds_[1] > 0 + ? setUint(setIds_[1], uint256(newCol_)) + : setUint(setIds_[2], uint256(newCol_)); // If setIds_[2] != 0, it will set the ID. + setIds_[3] > 0 + ? setUint(setIds_[3], uint256(newDebt_)) + : setUint(setIds_[4], uint256(newDebt_)); // If setIds_[4] != 0, it will set the ID. + + _eventName = "LogOperateWithIds(address,uint256,int256,int256,uint256[],uint256[])"; + _eventParam = abi.encode( + vaultAddress_, + nftId_, + newCol_, + newDebt_, + getIds_, + setIds_ + ); + } + + /** + * @dev Deposit, borrow, payback and withdraw asset from the vault. + * @notice Single function which handles supply, withdraw, borrow & payback + * @param vaultAddress_ Vault address. + * @param nftId_ NFT ID for interaction. If 0 then create new NFT/position. + * @param newCol_ New collateral. If positive then deposit, if negative then withdraw, if 0 then do nothing. + * For max deposit use type(uint25).max, for max withdraw use type(uint25).min. + * @param newDebt_ New debt. If positive then borrow, if negative then payback, if 0 then do nothing + * For max payback use type(uint25).min. + * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. + * Should always be positive. + */ + function operate( + address vaultAddress_, + uint256 nftId_, + int256 newCol_, + int256 newDebt_, + uint256 repayApproveAmt_ + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + IVault vault_ = IVault(vaultAddress_); + + IVault.ConstantViews memory vaultDetails_ = vault_.constantsView(); + + uint256 ethAmount_; + + bool isColMax_ = newCol_ == type(int256).max; + + // Deposit + if (newCol_ > 0) { + if (vaultDetails_.supplyToken == getEthAddr()) { + ethAmount_ = isColMax_ + ? address(this).balance + : uint256(newCol_); + } else { + if (isColMax_) { + newCol_ = int256( + TokenInterface(vaultDetails_.supplyToken).balanceOf( + address(this) + ) + ); + } + + TokenInterface(vaultDetails_.supplyToken).approve( + vaultAddress_, + uint256(newCol_) + ); + } + } + + bool isPaybackMax_ = newDebt_ == type(int256).min; + + // Payback + if (newDebt_ < 0) { + if (vaultDetails_.borrowToken == getEthAddr()) { + // Needs to be positive as it will be send in msg.value + ethAmount_ = isPaybackMax_ + ? repayApproveAmt_ + : uint256(-1 * newDebt_); + } else { + isPaybackMax_ + ? TokenInterface(vaultDetails_.borrowToken).approve( + vaultAddress_, + repayApproveAmt_ + ) + : TokenInterface(vaultDetails_.borrowToken).approve( + vaultAddress_, + uint256(-1 * newDebt_) + ); + } + } + + // Note max withdraw will be handled by Fluid contract + (nftId_, newCol_, newDebt_) = vault_.operate{value: ethAmount_}( + nftId_, + newCol_, + newDebt_, + address(this) + ); + + _eventName = "LogOperate(address,uint256,int256,int256)"; + _eventParam = abi.encode( + vaultAddress_, + nftId_, + newCol_, + newDebt_ + ); + } +} + +contract ConnectV2Fluid is FluidConnector { + string public constant name = "Fluid-v1.0"; +} From 0eb6f1d9c20407952be3fde4f9ea4b7781242014 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sat, 10 Feb 2024 18:17:02 +0400 Subject: [PATCH 2/7] feat: update fluid --- contracts/polygon/connectors/fluid/events.sol | 7 + contracts/polygon/connectors/fluid/main.sol | 99 ++- test/polygon/fluid/fluid.test.ts | 692 +++++++++--------- 3 files changed, 451 insertions(+), 347 deletions(-) diff --git a/contracts/polygon/connectors/fluid/events.sol b/contracts/polygon/connectors/fluid/events.sol index 3fcb301..c0055ef 100644 --- a/contracts/polygon/connectors/fluid/events.sol +++ b/contracts/polygon/connectors/fluid/events.sol @@ -3,6 +3,13 @@ pragma solidity ^0.8.2; contract Events { event LogOperate( + address vaultAddress, + uint256 nftId, + int256 newCol, + int256 newDebt + ); + + event LogOperateWithIds( address vaultAddress, uint256 nftId, int256 newCol, diff --git a/contracts/polygon/connectors/fluid/main.sol b/contracts/polygon/connectors/fluid/main.sol index 181c428..b67216a 100644 --- a/contracts/polygon/connectors/fluid/main.sol +++ b/contracts/polygon/connectors/fluid/main.sol @@ -29,6 +29,7 @@ abstract contract FluidConnector is Events, Stores { * @param newDebt_ New debt. If positive then borrow, if negative then payback, if 0 then do nothing * For max payback use type(uint25).min. * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. + * Should always be positive. * @param getIds_ Array of 5 elements to retrieve IDs: * Nft Id, Supply amount, Withdraw amount, Borrow Amount, Payback Amount * @param setIds_ Array of 5 elements to store IDs generated: @@ -85,6 +86,7 @@ abstract contract FluidConnector is Events, Stores { bool isColMax_ = newCol_ == type(int256).max; + // Deposit if (newCol_ > 0) { if (vaultDetails_.supplyToken == getMaticAddr()) { ethAmount_ = isColMax_ @@ -108,8 +110,10 @@ abstract contract FluidConnector is Events, Stores { bool isPaybackMax_ = newDebt_ == type(int256).min; + // Payback if (newDebt_ < 0) { if (vaultDetails_.borrowToken == getMaticAddr()) { + // Needs to be positive as it will be send in msg.value ethAmount_ = isPaybackMax_ ? repayApproveAmt_ : uint256(-1 * newDebt_); @@ -126,6 +130,7 @@ abstract contract FluidConnector is Events, Stores { } } + // Note max withdraw will be handled by Fluid contract (nftId_, newCol_, newDebt_) = vault_.operate{value: ethAmount_}( nftId_, newCol_, @@ -142,7 +147,7 @@ abstract contract FluidConnector is Events, Stores { ? setUint(setIds_[3], uint256(newDebt_)) : setUint(setIds_[4], uint256(newDebt_)); // If setIds_[4] != 0, it will set the ID. - _eventName = "LogOperate(address,uint256,int256,int256,uint256[],uint256[])"; + _eventName = "LogOperateWithIds(address,uint256,int256,int256,uint256[],uint256[])"; _eventParam = abi.encode( vaultAddress_, nftId_, @@ -152,6 +157,98 @@ abstract contract FluidConnector is Events, Stores { setIds_ ); } + + /** + * @dev Deposit, borrow, payback and withdraw asset from the vault. + * @notice Single function which handles supply, withdraw, borrow & payback + * @param vaultAddress_ Vault address. + * @param nftId_ NFT ID for interaction. If 0 then create new NFT/position. + * @param newCol_ New collateral. If positive then deposit, if negative then withdraw, if 0 then do nothing. + * For max deposit use type(uint25).max, for max withdraw use type(uint25).min. + * @param newDebt_ New debt. If positive then borrow, if negative then payback, if 0 then do nothing + * For max payback use type(uint25).min. + * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. + * Should always be positive. + */ + function operate( + address vaultAddress_, + uint256 nftId_, + int256 newCol_, + int256 newDebt_, + uint256 repayApproveAmt_ + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + IVault vault_ = IVault(vaultAddress_); + + IVault.ConstantViews memory vaultDetails_ = vault_.constantsView(); + + uint256 ethAmount_; + + bool isColMax_ = newCol_ == type(int256).max; + + // Deposit + if (newCol_ > 0) { + if (vaultDetails_.supplyToken == getMaticAddr()) { + ethAmount_ = isColMax_ + ? address(this).balance + : uint256(newCol_); + } else { + if (isColMax_) { + newCol_ = int256( + TokenInterface(vaultDetails_.supplyToken).balanceOf( + address(this) + ) + ); + } + + TokenInterface(vaultDetails_.supplyToken).approve( + vaultAddress_, + uint256(newCol_) + ); + } + } + + bool isPaybackMax_ = newDebt_ == type(int256).min; + + // Payback + if (newDebt_ < 0) { + if (vaultDetails_.borrowToken == getMaticAddr()) { + // Needs to be positive as it will be send in msg.value + ethAmount_ = isPaybackMax_ + ? repayApproveAmt_ + : uint256(-1 * newDebt_); + } else { + isPaybackMax_ + ? TokenInterface(vaultDetails_.borrowToken).approve( + vaultAddress_, + repayApproveAmt_ + ) + : TokenInterface(vaultDetails_.borrowToken).approve( + vaultAddress_, + uint256(-1 * newDebt_) + ); + } + } + + // Note max withdraw will be handled by Fluid contract + (nftId_, newCol_, newDebt_) = vault_.operate{value: ethAmount_}( + nftId_, + newCol_, + newDebt_, + address(this) + ); + + _eventName = "LogOperate(address,uint256,int256,int256)"; + _eventParam = abi.encode( + vaultAddress_, + nftId_, + newCol_, + newDebt_ + ); + } } contract ConnectV2FluidPolygon is FluidConnector { diff --git a/test/polygon/fluid/fluid.test.ts b/test/polygon/fluid/fluid.test.ts index d09deeb..a3605e8 100644 --- a/test/polygon/fluid/fluid.test.ts +++ b/test/polygon/fluid/fluid.test.ts @@ -1,7 +1,7 @@ import { expect } from "chai"; import hre from "hardhat"; import { abis } from "../../../scripts/constant/abis"; -import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; @@ -20,7 +20,7 @@ describe("Fluid", function () { let dsaWallet0: any; let instaConnectorsV2: any; let masterSigner: Signer; - const setIdMaticUsdc = "83478237"; + const setIdMaticUsdc = ethers.BigNumber.from("83478237"); const setIdWethUsdc = "83478249"; const setId3 = "85478249"; @@ -29,7 +29,7 @@ describe("Fluid", function () { const vaultWethMatic = "0x553437CB882E3aFbB67Abd135E067AFB0721fbf1"; const wethHolder = "0xdeD8C5159CA3673f543D0F72043E4c655b35b96A"; - const usdcHolder = "0xEc4Db437ADEE0420A28Afa8B87c74D3901F56AC4"; + const usdcHolder = "0xA67EFB69A4f58F568aAB1b9d51110102985835b0"; const WETH = "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619"; const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; @@ -127,7 +127,7 @@ describe("Fluid", function () { forking: { // @ts-ignore jsonRpcUrl: hre.config.networks.hardhat.forking.url, - blockNumber: 12796965, + blockNumber: 53327050, }, }, ], @@ -140,6 +140,7 @@ describe("Fluid", function () { addresses.core.connectorsV2, masterSigner ); + connector = await deployAndEnableConnector({ connectorName, contractArtifact: ConnectV2FluidPolygon__factory, @@ -186,8 +187,7 @@ describe("Fluid", function () { await wethToken.connect(wethHolderSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("20")); - expect(await wethToken.balanceOf(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("20")); - + expect(await wethToken.connect(wethHolderSigner).balanceOf(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("20")); }); it("Deposit 20 Usdc into DSA wallet", async function () { @@ -205,7 +205,7 @@ describe("Fluid", function () { await usdcToken.connect(usdcHolderSigner).transfer(dsaWallet0.address, parseUnits("20", 6)); - expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.gte(parseUnits("20", 6)); + expect(await usdcToken.connect(usdcHolderSigner).balanceOf(dsaWallet0.address)).to.be.gte(parseUnits("20", 6)); }); }); @@ -221,12 +221,12 @@ describe("Fluid", function () { method: "operate", args: [ vaultMaticUsdc, - 0, // new nft + '0', // new nft amtDeposit, // +1000 collateral - 0, // 0 debt - dsaWallet0.address, - 0, - setIdMaticUsdc + '0', // 0 debt + '0', + ['0','0','0','0','0'], + ['0',setIdMaticUsdc,'0','0','0'] ], }, ]; @@ -243,407 +243,407 @@ describe("Fluid", function () { }); // 1000 matic - it("should deposit max Matic in Fluid", async function () { - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultMaticUsdc, // matic-usdc vault - 0, // setIdMaticUsdc - ethers.constants.MaxUint256, // + max collateral - 0, // 0 debt - dsaWallet0.address, - setIdMaticUsdc, - 0 - ], - }, - ]; + // it("should deposit max Matic in Fluid", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultMaticUsdc, // matic-usdc vault + // 0, // setIdMaticUsdc + // ethers.constants.MaxUint256, // + max collateral + // 0, // 0 debt + // 0, + // [0,setIdMaticUsdc,0,0,0], + // [0,0,0,0,0] + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.lte( - parseEther("1") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.lte( + // parseEther("1") + // ); + // }); - // 0 matic + // // 0 matic - it("should deposit 10 Weth in Fluid", async function () { - const amtDeposit = parseEther("10"); + // it("should deposit 10 Weth in Fluid", async function () { + // const amtDeposit = parseEther("10"); - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultWethUsdc, - 0, // new nft - amtDeposit, // +10 collateral - 0, // 0 debt - dsaWallet0.address, - 0, - setIdWethUsdc - ], - }, - ]; + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultWethUsdc, + // 0, // new nft + // amtDeposit, // +10 collateral + // 0, // 0 debt + // 0, + // [0,0,0,0,0], + // [0,setIdWethUsdc,0,0,0] + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // await tx.wait(); - expect(await wethToken.balanceOf(dsaWallet0.address)).to.lte( - parseEther("10") - ); - }); + // expect(await wethToken.balanceOf(dsaWallet0.address)).to.lte( + // parseEther("10") + // ); + // }); - // 10 weth + // // 10 weth - it("should deposit max Weth in Fluid", async function () { - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultWethUsdc, - 0, // get id nft - ethers.constants.MaxUint256, // + max collateral - 0, // 0 debt - dsaWallet0.address, - setIdWethUsdc, - 0 - ], - }, - ]; + // it("should deposit max Weth in Fluid", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultWethUsdc, + // 0, // get id nft + // ethers.constants.MaxUint256, // + max collateral + // 0, // 0 debt + // 0, + // [0,setIdWethUsdc,0,0,0], + // [0,0,0,0,0] + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.lte( - parseEther("1") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.lte( + // parseEther("1") + // ); + // }); - // 0 weth + // // 0 weth - it("Should borrow USDC from Fluid", async function () { - const amtBorrow = parseUnits("100", 6); // 100 USDC + // it("Should borrow USDC from Fluid", async function () { + // const amtBorrow = parseUnits("100", 6); // 100 USDC - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultMaticUsdc, - 0, // nft id from getID - 0, // 0 collateral - amtBorrow, // +100 debt - dsaWallet0.address, - setIdMaticUsdc, - 0 - ], - }, - ]; + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultMaticUsdc, + // 0, // nft id from getID + // 0, // 0 collateral + // amtBorrow, // +100 debt + // 0, + // [0,0,0,0,0], + // [0,0,0,0,0] + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + // await tx.wait(); - expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.gte( - parseUnits("120", 6) - ); - }); + // expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.gte( + // parseUnits("120", 6) + // ); + // }); - // 120 usdc + // // 120 usdc - it("Should deposit WETH and borrow MATIC from Fluid", async function () { - await hre.network.provider.request({ - method: "hardhat_impersonateAccount", - params: [wethHolder] - }); + // it("Should deposit WETH and borrow MATIC from Fluid", async function () { + // await hre.network.provider.request({ + // method: "hardhat_impersonateAccount", + // params: [wethHolder] + // }); - wethHolderSigner = await ethers.getSigner(wethHolder); + // wethHolderSigner = await ethers.getSigner(wethHolder); - await wethToken.connect(wethHolderSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("11")); + // await wethToken.connect(wethHolderSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("11")); - const amtDeposit = parseEther("10"); // 10 Weth - const amtBorrow = parseEther("100"); // 100 Matic + // const amtDeposit = parseEther("10"); // 10 Weth + // const amtBorrow = parseEther("100"); // 100 Matic - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultWethMatic, - 0, // new nft id - amtDeposit, // 10 collateral - amtBorrow, // +100 debt - dsaWallet0.address, - 0, - setId3 - ], - }, - ]; + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultWethMatic, + // 0, // new nft id + // amtDeposit, // 10 collateral + // amtBorrow, // +100 debt + // 0, + // [0,0,0,0,0], + // [0,0,0,setId3,0] + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( - parseEther("100") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + // parseEther("100") + // ); + // }); - // 120 usdc, 100 matic + // // 120 usdc, 100 matic - it("Should payback Matic in Fluid", async function () { - const amtPayback = ethers.BigNumber.from(parseEther("50")).mul(-1); // 50 Matic + // it("Should payback Matic in Fluid", async function () { + // const amtPayback = ethers.BigNumber.from(parseEther("50")).mul(-1); // 50 Matic - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultWethMatic, - 0, // nft id from setId3 - 0, // 0 collateral - amtPayback, // - 50 debt - dsaWallet0.address, - setId3, - 0 - ], - }, - ]; + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultWethMatic, + // 0, // nft id from setId3 + // 0, // 0 collateral + // amtPayback, // - 50 debt + // ethers.BigNumber.from(parseEther("50")), + // [0,0,0,0,setId3], + // [0,0,0,0,0] + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( - ethers.utils.parseEther("50") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + // ethers.utils.parseEther("50") + // ); + // }); - // 120 usdc, 50 matic + // // 120 usdc, 50 matic - it("Should payback max Matic in Fluid", async function () { - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultWethMatic, - 0, // nft id from setId3 - 0, // 0 collateral - ethers.constants.MinInt256, // min Int - dsaWallet0.address, - setId3, - 0 - ], - }, - ]; + // it("Should payback max Matic in Fluid", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultWethMatic, + // 0, // nft id from setId3 + // 0, // 0 collateral + // ethers.constants.MinInt256, // min Int + // ethers.BigNumber.from(parseEther("50")), + // [0,0,0,0,0], + // [0,0,0,0,0] + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( - ethers.utils.parseEther("1") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + // ethers.utils.parseEther("1") + // ); + // }); - // 120 usdc, 0 matic + // 120 usdc, 0 matic///////// - it("Should payback Usdc in Fluid", async function () { - const amtPayback = ethers.BigNumber.from(parseUnits("60", 6)).mul(-1); // 60 usdc + // it("Should payback Usdc in Fluid", async function () { + // const amtPayback = ethers.BigNumber.from(parseUnits("60", 6)).mul(-1); // 60 usdc - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultMaticUsdc, - 0, // nft id from setIdWethUsdc - 0, // 0 collateral - amtPayback, // - 60 debt - dsaWallet0.address, - setIdMaticUsdc, - 0 - ], - }, - ]; + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultMaticUsdc, + // 0, // nft id from setIdWethUsdc + // 0, // 0 collateral + // amtPayback, // - 60 debt + // dsaWallet0.address, + // setIdMaticUsdc, + // 0 + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + // await tx.wait(); - expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.lte(parseUnits("60", 6)); - }); + // expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.lte(parseUnits("60", 6)); + // }); - // 60 usdc, 0 matic + // // 60 usdc, 0 matic - it("Should payback max Matic in Fluid", async function () { - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultMaticUsdc, - 0, // nft id from setIdWethUsdc - 0, // 0 collateral - ethers.constants.MinInt256, // min Int - dsaWallet0.address, - setIdMaticUsdc, - 0 - ], - }, - ]; + // it("Should payback max Matic in Fluid", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultMaticUsdc, + // 0, // nft id from setIdWethUsdc + // 0, // 0 collateral + // ethers.constants.MinInt256, // min Int + // dsaWallet0.address, + // setIdMaticUsdc, + // 0 + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + // await tx.wait(); - expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.lte(parseUnits("1", 6)); - }); + // expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.lte(parseUnits("1", 6)); + // }); - // 0 usdc, 0 matic + // // 0 usdc, 0 matic - it("Should withdraw Matic from Fluid", async function () { - const amt = ethers.BigNumber.from(parseEther("100")).mul(-1); // 100 Matic + // it("Should withdraw Matic from Fluid", async function () { + // const amt = ethers.BigNumber.from(parseEther("100")).mul(-1); // 100 Matic - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultMaticUsdc, - 0, // nft id from setIdMaticUsdc - amt, // - 100 collateral - 0, // 0 debt - dsaWallet0.address, - setIdMaticUsdc, - 0 - ], - }, - ]; + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultMaticUsdc, + // 0, // nft id from setIdMaticUsdc + // amt, // - 100 collateral + // 0, // 0 debt + // dsaWallet0.address, + // setIdMaticUsdc, + // 0 + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( - parseEther("100") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( + // parseEther("100") + // ); + // }); - // 0 usdc, 100 matic + // // 0 usdc, 100 matic - it("Should withdraw max Matic from Fluid", async function () { - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultMaticUsdc, - 0, // nft id from setIdMaticUsdc - ethers.constants.MinInt256, // min integer value - 0, // 0 debt - dsaWallet0.address, - setIdMaticUsdc, - 0 - ], - }, - ]; + // it("Should withdraw max Matic from Fluid", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultMaticUsdc, + // 0, // nft id from setIdMaticUsdc + // ethers.constants.MinInt256, // min integer value + // 0, // 0 debt + // dsaWallet0.address, + // setIdMaticUsdc, + // 0 + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( - parseEther("1000") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( + // parseEther("1000") + // ); + // }); - // 0 usdc, 1000 matic + // // 0 usdc, 1000 matic - it("Should withdraw WETH from Fluid", async function () { - const amt = ethers.BigNumber.from(parseEther("10")).mul(-1); // 10 Weth + // it("Should withdraw WETH from Fluid", async function () { + // const amt = ethers.BigNumber.from(parseEther("10")).mul(-1); // 10 Weth - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultWethUsdc, - 0, // nft id from setIdWethUsdc - amt, // -10 collateral - 0, // 0 debt - dsaWallet0.address, - setIdWethUsdc, - 0 - ], - }, - ]; + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultWethUsdc, + // 0, // nft id from setIdWethUsdc + // amt, // -10 collateral + // 0, // 0 debt + // dsaWallet0.address, + // setIdWethUsdc, + // 0 + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // await tx.wait(); - expect(await wethToken.balanceOf(dsaWallet0.address)).to.eq( - parseEther("10") - ); - }); + // expect(await wethToken.balanceOf(dsaWallet0.address)).to.eq( + // parseEther("10") + // ); + // }); - // 0 usdc, 1000 matic, 10 weth + // // 0 usdc, 1000 matic, 10 weth - it("Should withdraw max WETH from Fluid", async function () { - const spells = [ - { - connector: connectorName, - method: "operate", - args: [ - vaultWethUsdc, - 0, // nft id from setIdWethUsdc - ethers.constants.MinInt256, // min integer value - 0, // 0 debt - dsaWallet0.address, - setIdWethUsdc, - 0 - ], - }, - ]; + // it("Should withdraw max WETH from Fluid", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "operate", + // args: [ + // vaultWethUsdc, + // 0, // nft id from setIdWethUsdc + // ethers.constants.MinInt256, // min integer value + // 0, // 0 debt + // dsaWallet0.address, + // setIdWethUsdc, + // 0 + // ], + // }, + // ]; - const tx = await dsaWallet0 - .connect(wallet0) - .cast(...encodeSpells(spells), wallet1.getAddress()); + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); - await tx.wait(); + // await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( - parseEther("29") - ); - }); + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( + // parseEther("29") + // ); + // }); // 0 usdc, 1000 matic, 30 weth From 923b406679bc0fa3f2fc5a93234ac0eb65d31a68 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sat, 10 Feb 2024 23:06:30 +0400 Subject: [PATCH 3/7] feat: update --- contracts/mainnet/connectors/fluid/main.sol | 2 +- contracts/polygon/connectors/fluid/main.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/mainnet/connectors/fluid/main.sol b/contracts/mainnet/connectors/fluid/main.sol index 9bb4c55..1c09d49 100644 --- a/contracts/mainnet/connectors/fluid/main.sol +++ b/contracts/mainnet/connectors/fluid/main.sol @@ -170,7 +170,7 @@ abstract contract FluidConnector is Events, Stores { * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. * Should always be positive. */ - function operate( + function operate2( address vaultAddress_, uint256 nftId_, int256 newCol_, diff --git a/contracts/polygon/connectors/fluid/main.sol b/contracts/polygon/connectors/fluid/main.sol index b67216a..a27069d 100644 --- a/contracts/polygon/connectors/fluid/main.sol +++ b/contracts/polygon/connectors/fluid/main.sol @@ -170,7 +170,7 @@ abstract contract FluidConnector is Events, Stores { * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. * Should always be positive. */ - function operate( + function operate2( address vaultAddress_, uint256 nftId_, int256 newCol_, From 4b7d9c6263fff0eae6f97fe970665786622567f3 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sat, 10 Feb 2024 23:12:47 +0400 Subject: [PATCH 4/7] feat: update function name --- contracts/mainnet/connectors/fluid/main.sol | 4 ++-- contracts/polygon/connectors/fluid/main.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/mainnet/connectors/fluid/main.sol b/contracts/mainnet/connectors/fluid/main.sol index 1c09d49..8b57dda 100644 --- a/contracts/mainnet/connectors/fluid/main.sol +++ b/contracts/mainnet/connectors/fluid/main.sol @@ -35,7 +35,7 @@ abstract contract FluidConnector is Events, Stores { * @param setIds_ Array of 5 elements to store IDs generated: * Nft Id, Supply amount, Withdraw amount, Borrow Amount, Payback Amount */ - function operate( + function operateWithIds( address vaultAddress_, uint256 nftId_, int256 newCol_, @@ -170,7 +170,7 @@ abstract contract FluidConnector is Events, Stores { * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. * Should always be positive. */ - function operate2( + function operate( address vaultAddress_, uint256 nftId_, int256 newCol_, diff --git a/contracts/polygon/connectors/fluid/main.sol b/contracts/polygon/connectors/fluid/main.sol index a27069d..b3b6567 100644 --- a/contracts/polygon/connectors/fluid/main.sol +++ b/contracts/polygon/connectors/fluid/main.sol @@ -35,7 +35,7 @@ abstract contract FluidConnector is Events, Stores { * @param setIds_ Array of 5 elements to store IDs generated: * Nft Id, Supply amount, Withdraw amount, Borrow Amount, Payback Amount */ - function operate( + function operateWithIds( address vaultAddress_, uint256 nftId_, int256 newCol_, @@ -170,7 +170,7 @@ abstract contract FluidConnector is Events, Stores { * @param repayApproveAmt_ In case of max amount for payback, this amount will be approved for spending. * Should always be positive. */ - function operate2( + function operate( address vaultAddress_, uint256 nftId_, int256 newCol_, From cf61ac8b2257d0a2c048d310ec1d202ad8686012 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sun, 11 Feb 2024 14:33:32 +0400 Subject: [PATCH 5/7] feat: update mainnet connector --- contracts/mainnet/connectors/fluid/main.sol | 22 +++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/contracts/mainnet/connectors/fluid/main.sol b/contracts/mainnet/connectors/fluid/main.sol index 8b57dda..0cab810 100644 --- a/contracts/mainnet/connectors/fluid/main.sol +++ b/contracts/mainnet/connectors/fluid/main.sol @@ -92,6 +92,8 @@ abstract contract FluidConnector is Events, Stores { ethAmount_ = isColMax_ ? address(this).balance : uint256(newCol_); + + newCol_ = int256(ethAmount_); } else { if (isColMax_) { newCol_ = int256( @@ -108,24 +110,24 @@ abstract contract FluidConnector is Events, Stores { } } - bool isPaybackMax_ = newDebt_ == type(int256).min; + bool isPaybackMin_ = newDebt_ == type(int256).min; // Payback if (newDebt_ < 0) { if (vaultDetails_.borrowToken == getEthAddr()) { // Needs to be positive as it will be send in msg.value - ethAmount_ = isPaybackMax_ + ethAmount_ = isPaybackMin_ ? repayApproveAmt_ - : uint256(-1 * newDebt_); + : uint256(-newDebt_); } else { - isPaybackMax_ + isPaybackMin_ ? TokenInterface(vaultDetails_.borrowToken).approve( vaultAddress_, repayApproveAmt_ ) : TokenInterface(vaultDetails_.borrowToken).approve( vaultAddress_, - uint256(-1 * newDebt_) + uint256(-newDebt_) ); } } @@ -211,24 +213,24 @@ abstract contract FluidConnector is Events, Stores { } } - bool isPaybackMax_ = newDebt_ == type(int256).min; + bool isPaybackMin_ = newDebt_ == type(int256).min; // Payback if (newDebt_ < 0) { if (vaultDetails_.borrowToken == getEthAddr()) { // Needs to be positive as it will be send in msg.value - ethAmount_ = isPaybackMax_ + ethAmount_ = isPaybackMin_ ? repayApproveAmt_ - : uint256(-1 * newDebt_); + : uint256(-newDebt_); } else { - isPaybackMax_ + isPaybackMin_ ? TokenInterface(vaultDetails_.borrowToken).approve( vaultAddress_, repayApproveAmt_ ) : TokenInterface(vaultDetails_.borrowToken).approve( vaultAddress_, - uint256(-1 * newDebt_) + uint256(-newDebt_) ); } } From 6b7a9dbb45c51e3b00caf89ff257c4bb4c74003b Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sun, 11 Feb 2024 21:41:24 +0400 Subject: [PATCH 6/7] feat: add instapool v5 --- .../connectors/instapool_v5/events.sol | 10 ++ .../connectors/instapool_v5/interfaces.sol | 11 ++ .../mainnet/connectors/instapool_v5/main.sol | 136 ++++++++++++++++++ .../connectors/instapool_v5/variables.sol | 14 ++ 4 files changed, 171 insertions(+) create mode 100644 contracts/mainnet/connectors/instapool_v5/events.sol create mode 100644 contracts/mainnet/connectors/instapool_v5/interfaces.sol create mode 100644 contracts/mainnet/connectors/instapool_v5/main.sol create mode 100644 contracts/mainnet/connectors/instapool_v5/variables.sol diff --git a/contracts/mainnet/connectors/instapool_v5/events.sol b/contracts/mainnet/connectors/instapool_v5/events.sol new file mode 100644 index 0000000..5d5bd77 --- /dev/null +++ b/contracts/mainnet/connectors/instapool_v5/events.sol @@ -0,0 +1,10 @@ +//SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +contract Events { + event LogFlashBorrow(address token, uint256 tokenAmt); + event LogFlashPayback(address token, uint256 tokenAmt); + + event LogFlashMultiBorrow(address[] token, uint256[] tokenAmts); + event LogFlashMultiPayback(address[] token, uint256[] tokenAmts); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/instapool_v5/interfaces.sol b/contracts/mainnet/connectors/instapool_v5/interfaces.sol new file mode 100644 index 0000000..58c7a98 --- /dev/null +++ b/contracts/mainnet/connectors/instapool_v5/interfaces.sol @@ -0,0 +1,11 @@ +//SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +interface InstaFlashV5Interface { + function flashLoan(address[] memory tokens, uint256[] memory amts, uint route, bytes memory data, bytes memory extraData) external; +} + +interface AccountInterface { + function enable(address) external; + function disable(address) external; +} diff --git a/contracts/mainnet/connectors/instapool_v5/main.sol b/contracts/mainnet/connectors/instapool_v5/main.sol new file mode 100644 index 0000000..8fce0d1 --- /dev/null +++ b/contracts/mainnet/connectors/instapool_v5/main.sol @@ -0,0 +1,136 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +/** + * @title Instapool. + * @dev Inbuilt Flash Loan in DSA + */ + +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { AccountInterface } from "./interfaces.sol"; +import { Stores } from "../../common/stores.sol"; +import { Variables } from "./variables.sol"; +import { Events } from "./events.sol"; + +contract LiquidityResolver is Stores, Variables, Events { + using SafeERC20 for IERC20; + + /** + * @dev Borrow Flashloan and Cast spells. + * @notice Borrow Flashloan and Cast spells. + * @param token Token Address. + * @param amt Token Amount. + * @param route Flashloan source route. + * @param data targets & data for cast. + * @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes + */ + function flashBorrowAndCast( + address token, + uint amt, + uint route, + bytes memory data, + bytes memory extraData + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + AccountInterface(address(this)).enable(address(instaPool)); + (string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[])); + + bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool)); + + address[] memory tokens_ = new address[](1); + tokens_[0] = token; + uint[] memory amts_ = new uint[](1); + amts_[0] = amt; + instaPool.flashLoan(tokens_, amts_, route, callData_, extraData); + + AccountInterface(address(this)).disable(address(instaPool)); + + _eventName = "LogFlashBorrow(address,uint256)"; + _eventParam = abi.encode(token, amt); + } + + /** + * @dev Return token to InstaPool. + * @notice Return token to InstaPool. + * @param token Token Address. + * @param amt Token Amount. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function flashPayback( + address token, + uint amt, + uint getId, + uint setId + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + uint _amt = getUint(getId, amt); + + IERC20 tokenContract = IERC20(token); + + tokenContract.safeTransfer(address(instaPool), _amt); + + setUint(setId, _amt); + + _eventName = "LogFlashPayback(address,uint256)"; + _eventParam = abi.encode(token, amt); + } + + /** + * @dev Borrow multi-tokens Flashloan and Cast spells. + * @notice Borrow multi-tokens Flashloan and Cast spells. + * @param tokens_ Array of Token Addresses. + * @param amts_ Array of Token Amounts. + * @param route Flashloan source route. + * @param data targets & data for cast. + * @param extraData to be kept bytes(0) in most cases. Can be useful to decide data for some particular routes + */ + function flashMultiBorrowAndCast( + address[] memory tokens_, + uint[] memory amts_, + uint route, + bytes memory data, + bytes memory extraData + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + AccountInterface(address(this)).enable(address(instaPool)); + (string[] memory _targets, bytes[] memory callDatas) = abi.decode(data, (string[], bytes[])); + + bytes memory callData_ = abi.encodeWithSignature("cast(string[],bytes[],address)", _targets, callDatas, address(instaPool)); + + instaPool.flashLoan(tokens_, amts_, route, callData_, extraData); + + AccountInterface(address(this)).disable(address(instaPool)); + _eventName = "LogFlashMultiBorrow(address[],uint256[])"; + _eventParam = abi.encode(tokens_, amts_); + } + + /** + * @dev Return multi-tokens to InstaPool. + * @notice Return multi-tokens to InstaPool. + * @param tokens_ Array of Token Addresses. + * @param amts_ Array of Token Amounts. + * @param getIds Array of getId token amounts. + * @param setIds Array of setId token amounts. + */ + function flashMultiPayback( + address[] memory tokens_, + uint[] memory amts_, + uint[] memory getIds, + uint[] memory setIds + ) external payable returns (string memory _eventName, bytes memory _eventParam) { + for (uint i = 0; i < tokens_.length; i++) { + amts_[i] = getUint(getIds[i], amts_[i]); + + IERC20(tokens_[i]).safeTransfer(address(instaPool), amts_[i]); + + setUint(setIds[i], amts_[i]); + } + + _eventName = "LogFlashMultiPayback(address[],uint256[])"; + _eventParam = abi.encode(tokens_, amts_); + } + +} + +contract ConnectV2InstaPoolV5 is LiquidityResolver { + string public name = "Instapool-v5"; +} diff --git a/contracts/mainnet/connectors/instapool_v5/variables.sol b/contracts/mainnet/connectors/instapool_v5/variables.sol new file mode 100644 index 0000000..f2182a4 --- /dev/null +++ b/contracts/mainnet/connectors/instapool_v5/variables.sol @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { InstaFlashV5Interface } from "./interfaces.sol"; + +contract Variables { + + /** + * @dev Instapool contract proxy + */ + InstaFlashV5Interface public constant instaPool = + InstaFlashV5Interface(0xAB50Dd1C57938218627Df2311ef65b4e2e84aF48); + +} \ No newline at end of file From 4f27d6fa78ef2c501bc4053c560ecb28be729f15 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Mon, 12 Feb 2024 01:02:33 +0400 Subject: [PATCH 7/7] feat: update fluid connector --- contracts/mainnet/connectors/fluid/main.sol | 2 + contracts/polygon/connectors/fluid/main.sol | 32 +- test/polygon/fluid/fluid.test.ts | 777 +++++++++++--------- 3 files changed, 440 insertions(+), 371 deletions(-) diff --git a/contracts/mainnet/connectors/fluid/main.sol b/contracts/mainnet/connectors/fluid/main.sol index 0cab810..265ce2f 100644 --- a/contracts/mainnet/connectors/fluid/main.sol +++ b/contracts/mainnet/connectors/fluid/main.sol @@ -197,6 +197,8 @@ abstract contract FluidConnector is Events, Stores { ethAmount_ = isColMax_ ? address(this).balance : uint256(newCol_); + + newCol_ = int256(ethAmount_); } else { if (isColMax_) { newCol_ = int256( diff --git a/contracts/polygon/connectors/fluid/main.sol b/contracts/polygon/connectors/fluid/main.sol index b3b6567..64120f9 100644 --- a/contracts/polygon/connectors/fluid/main.sol +++ b/contracts/polygon/connectors/fluid/main.sol @@ -82,16 +82,18 @@ abstract contract FluidConnector is Events, Stores { IVault.ConstantViews memory vaultDetails_ = vault_.constantsView(); - uint256 ethAmount_; + uint256 maticAmount_; bool isColMax_ = newCol_ == type(int256).max; // Deposit if (newCol_ > 0) { if (vaultDetails_.supplyToken == getMaticAddr()) { - ethAmount_ = isColMax_ + maticAmount_ = isColMax_ ? address(this).balance : uint256(newCol_); + + newCol_ = int256(maticAmount_); } else { if (isColMax_) { newCol_ = int256( @@ -108,30 +110,30 @@ abstract contract FluidConnector is Events, Stores { } } - bool isPaybackMax_ = newDebt_ == type(int256).min; + bool isPaybackMin_ = newDebt_ == type(int256).min; // Payback if (newDebt_ < 0) { if (vaultDetails_.borrowToken == getMaticAddr()) { // Needs to be positive as it will be send in msg.value - ethAmount_ = isPaybackMax_ + maticAmount_ = isPaybackMin_ ? repayApproveAmt_ - : uint256(-1 * newDebt_); + : uint256(-newDebt_); } else { - isPaybackMax_ + isPaybackMin_ ? TokenInterface(vaultDetails_.borrowToken).approve( vaultAddress_, repayApproveAmt_ ) : TokenInterface(vaultDetails_.borrowToken).approve( vaultAddress_, - uint256(-1 * newDebt_) + uint256(-newDebt_) ); } } // Note max withdraw will be handled by Fluid contract - (nftId_, newCol_, newDebt_) = vault_.operate{value: ethAmount_}( + (nftId_, newCol_, newDebt_) = vault_.operate{value: maticAmount_}( nftId_, newCol_, newDebt_, @@ -185,16 +187,18 @@ abstract contract FluidConnector is Events, Stores { IVault.ConstantViews memory vaultDetails_ = vault_.constantsView(); - uint256 ethAmount_; + uint256 maticAmount_; bool isColMax_ = newCol_ == type(int256).max; // Deposit if (newCol_ > 0) { if (vaultDetails_.supplyToken == getMaticAddr()) { - ethAmount_ = isColMax_ + maticAmount_ = isColMax_ ? address(this).balance : uint256(newCol_); + + newCol_ = int256(maticAmount_); } else { if (isColMax_) { newCol_ = int256( @@ -211,17 +215,17 @@ abstract contract FluidConnector is Events, Stores { } } - bool isPaybackMax_ = newDebt_ == type(int256).min; + bool isPaybackMin_ = newDebt_ == type(int256).min; // Payback if (newDebt_ < 0) { if (vaultDetails_.borrowToken == getMaticAddr()) { // Needs to be positive as it will be send in msg.value - ethAmount_ = isPaybackMax_ + maticAmount_ = isPaybackMin_ ? repayApproveAmt_ : uint256(-1 * newDebt_); } else { - isPaybackMax_ + isPaybackMin_ ? TokenInterface(vaultDetails_.borrowToken).approve( vaultAddress_, repayApproveAmt_ @@ -234,7 +238,7 @@ abstract contract FluidConnector is Events, Stores { } // Note max withdraw will be handled by Fluid contract - (nftId_, newCol_, newDebt_) = vault_.operate{value: ethAmount_}( + (nftId_, newCol_, newDebt_) = vault_.operate{value: maticAmount_}( nftId_, newCol_, newDebt_, diff --git a/test/polygon/fluid/fluid.test.ts b/test/polygon/fluid/fluid.test.ts index a3605e8..43aebc4 100644 --- a/test/polygon/fluid/fluid.test.ts +++ b/test/polygon/fluid/fluid.test.ts @@ -11,6 +11,7 @@ import { encodeSpells } from "../../../scripts/tests/encodeSpells"; import { constants } from "../../../scripts/constant/constant"; import { network, ethers } from "hardhat"; import type { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; describe("Fluid", function () { const connectorName = "FLUID"; @@ -20,13 +21,15 @@ describe("Fluid", function () { let dsaWallet0: any; let instaConnectorsV2: any; let masterSigner: Signer; - const setIdMaticUsdc = ethers.BigNumber.from("83478237"); + const setIdMaticUsdc = "83478237"; const setIdWethUsdc = "83478249"; const setId3 = "85478249"; + const setId4 = "55478249"; - const vaultMaticUsdc = "0x2226FFAE044B9fd4ED991aDf20CAACF8E8302510"; - const vaultWethUsdc = "0x10D97a8236624222F681C12Eea4Ddac2BDD0471B"; - const vaultWethMatic = "0x553437CB882E3aFbB67Abd135E067AFB0721fbf1"; + const vaultMaticUsdc = "0xAf047A21CE590B36FE894dd6fa350b57Ea5Cb0aa"; + const vaultWethUsdc = "0xEad5D80db075a905c141b37cE903d621952eA3f6"; + const vaultWethMatic = "0x23918014AF7610e31e58A9DC9f9A7DdbfcA4087e"; + const vaultUsdcMatic = "0x6395Ddb6161CeF6e64D4c027fbBa26CC76F18148"; const wethHolder = "0xdeD8C5159CA3673f543D0F72043E4c655b35b96A"; const usdcHolder = "0xA67EFB69A4f58F568aAB1b9d51110102985835b0"; @@ -212,21 +215,21 @@ describe("Fluid", function () { // 2000 matic, 20 weth, 20 usdc describe("Main", function () { - it("should deposit 1000 Matic in Fluid", async function () { + it("should deposit 1000 Matic in Fluid in MATIC-USDC", async function () { const amtDeposit = parseEther("1000"); const spells = [ { connector: connectorName, - method: "operate", + method: "operateWithIds", args: [ vaultMaticUsdc, '0', // new nft amtDeposit, // +1000 collateral '0', // 0 debt '0', - ['0','0','0','0','0'], - ['0',setIdMaticUsdc,'0','0','0'] + ['0', '0', '0', '0', '0'], + [setIdMaticUsdc, '0', '0', '0', '0'] // set NFT ID of position ], }, ]; @@ -241,303 +244,388 @@ describe("Fluid", function () { parseEther("1000") ); }); - // 1000 matic + // 1000 matic, 20 weth, 20 usdc - // it("should deposit max Matic in Fluid", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultMaticUsdc, // matic-usdc vault - // 0, // setIdMaticUsdc - // ethers.constants.MaxUint256, // + max collateral - // 0, // 0 debt - // 0, - // [0,setIdMaticUsdc,0,0,0], - // [0,0,0,0,0] - // ], - // }, - // ]; + it("should deposit max Matic in Fluid in MATIC-USDC", async function () { + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultMaticUsdc, // matic-usdc vault + 0, // NFT ID from setIdMaticUsdc + ethers.constants.MaxInt256, // + max collateral + 0, // 0 debt + 0, + [setIdMaticUsdc, '0', '0', '0', '0'], + [setIdMaticUsdc, '0', '0', '0', '0'] + ], + }, + ]; - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); + await tx.wait(); - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.lte( - // parseEther("1") - // ); - // }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.lte( + parseEther("1") + ); + }); - // // 0 matic + // // 0 matic, 20 weth, 20 usdc - // it("should deposit 10 Weth in Fluid", async function () { - // const amtDeposit = parseEther("10"); + it("should deposit 9 Weth in Fluid in WETH-USDC", async function () { + const amtDeposit = parseEther("9"); - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultWethUsdc, - // 0, // new nft - // amtDeposit, // +10 collateral - // 0, // 0 debt - // 0, - // [0,0,0,0,0], - // [0,setIdWethUsdc,0,0,0] - // ], - // }, - // ]; + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethUsdc, + 0, // New nft for WETH-USDC market + amtDeposit, // +10 collateral + 0, // 0 debt + 0, // 0 repay + ['0', '0', '0', '0', '0'], + [setIdWethUsdc, '0', '0', '0', '0'] + ], + }, + ]; - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); + await tx.wait(); - // expect(await wethToken.balanceOf(dsaWallet0.address)).to.lte( - // parseEther("10") - // ); - // }); + expect(await wethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.lte( + parseEther("11") + ); + }); - // // 10 weth + // // 0 matic, 11 weth, 20 usdc - // it("should deposit max Weth in Fluid", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultWethUsdc, - // 0, // get id nft - // ethers.constants.MaxUint256, // + max collateral - // 0, // 0 debt - // 0, - // [0,setIdWethUsdc,0,0,0], - // [0,0,0,0,0] - // ], - // }, - // ]; + it("should deposit max Weth in Fluid in WETH-USDC", async function () { + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethUsdc, + 0, // get nft id + ethers.constants.MaxInt256, // + max collateral + 0, // 0 debt + 0, + [setIdWethUsdc, '0', '0', '0', '0'], + [setIdWethUsdc, '0', '0', '0', '0'] + ], + }, + ]; - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); + await tx.wait(); - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.lte( - // parseEther("1") - // ); - // }); + expect(await wethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.lte( + parseEther("1") + ); + }); - // // 0 weth + // // 0 matic, 0 weth, 20 usdc - // it("Should borrow USDC from Fluid", async function () { - // const amtBorrow = parseUnits("100", 6); // 100 USDC + it("should deposit 10 USDC in Fluid in USDC-MATIC", async function () { + const spells = [ + { + connector: connectorName, + method: "operate", + args: [ + vaultUsdcMatic, + 0, // get nft id + parseUnits("10", 6), // + max collateral + 0, // 0 debt + 0 + ], + }, + ]; - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultMaticUsdc, - // 0, // nft id from getID - // 0, // 0 collateral - // amtBorrow, // +100 debt - // 0, - // [0,0,0,0,0], - // [0,0,0,0,0] - // ], - // }, - // ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); + await tx.wait(); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.lte( + parseEther("10") + ); + }); + + // // 0 matic, 0 weth, 10 usdc + + it("Should borrow 0.1 USDC from Fluid in WETH-USDC", async function () { + const amtBorrow = parseUnits("0.1", 6); // 0.1 USDC + + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethUsdc, + 0, // nft ID from getID + 0, // 0 collateral + amtBorrow, // +0.1 debt + 0, + [setIdWethUsdc, '0', '0', '0', '0'], + [setIdWethUsdc, '0', '0', '0', '0'] + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); - // expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.gte( - // parseUnits("120", 6) - // ); - // }); + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.gte( + parseUnits("10", 6) + ); + }); - // // 120 usdc + // // 0 matic, 0 weth, 10.1 usdc - // it("Should deposit WETH and borrow MATIC from Fluid", async function () { - // await hre.network.provider.request({ - // method: "hardhat_impersonateAccount", - // params: [wethHolder] + it("Should deposit 5 WETH and borrow 0.1 MATIC from Fluid in WETH-MATIC", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethHolder] + }); + + wethHolderSigner = await ethers.getSigner(wethHolder); + + await wethToken.connect(wethHolderSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + + const amtDeposit = parseEther("5"); // 5 Weth + const amtBorrow = parseEther("0.1"); // 100 Matic + + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethMatic, + 0, // new nft id + amtDeposit, // 10 collateral + amtBorrow, // +100 debt + 0, + ['0', '0', '0', '0', '0'], + [setId3, '0', '0', '0', '0'] + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("0.099") + ); + + expect(await wethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.lte( + parseEther("6") + ); + }); + + // // 0.1 matic, 5 weth, 10.1 usdc + + it("Should deposit 5 WETH and borrow 0.1 MATIC from Fluid in WETH-MATIC", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethHolder] + }); + + wethHolderSigner = await ethers.getSigner(wethHolder); + + await wethToken.connect(wethHolderSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + + const amtDeposit = parseEther("5"); // 5 Weth + const amtBorrow = parseEther("0.1"); // 0.1 Matic + + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethMatic, + 0, // new nft id + amtDeposit, // 10 collateral + amtBorrow, // +0.1 debt + 0, + ['0', '0', '0', '0', '0'], + [setId4, '0', '0', '0', '0'] + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("0.19") + ); + + expect(await wethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.lte( + parseEther("11") + ); + }); + + // // 0.2 matic, 10 weth, 10.1 usdc + + it("Should payback 0.04 Matic in Fluid in WETH-MATIC", async function () { + const amtPayback = new BigNumber(parseEther("0.04").toString()).multipliedBy(-1); // 0.04 Matic + + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethMatic, + 0, // nft id from setId3 + 0, // 0 collateral + amtPayback, // - 0.04 debt + new BigNumber(parseEther("0.04").toString()), + [setId3, '0', '0', '0', '0'], + [setId3, '0', '0', '0', '0'] + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("0.2") + ); + }); + + // // 0.16 matic, 10 weth, 10.1 usdc + + // it("Should payback max Matic in Fluid in WETH-MATIC", async function () { + // await wallet0.sendTransaction({ + // to: dsaWallet0.address, + // value: parseEther("1"), // }); - - // wethHolderSigner = await ethers.getSigner(wethHolder); - - // await wethToken.connect(wethHolderSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("11")); - // const amtDeposit = parseEther("10"); // 10 Weth - // const amtBorrow = parseEther("100"); // 100 Matic - // const spells = [ // { // connector: connectorName, - // method: "operate", + // method: "operateWithIds", // args: [ // vaultWethMatic, - // 0, // new nft id - // amtDeposit, // 10 collateral - // amtBorrow, // +100 debt + // 0, // nft id from setId3 + // 0, // 0 collateral + // ethers.constants.MinInt256, // min Int + // new BigNumber(parseEther("0.7").toString()), + // [setId3, '0', '0', '0', '0'], + // ['0', '0', '0', '0', '0'] + // ], + // }, + // ]; + + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + // await tx.wait(); + + // // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + // // ethers.utils.parseEther("0.97") + // // ); + // }); + + // // 0.96 matic, 10 weth, 10.1 usdc + + it("Should payback 0.05 Usdc in Fluid in WETH-USDC", async function () { + const amtPayback = new BigNumber(parseUnits("0.05", 6).toString()).multipliedBy(-1); // 0.05 usdc + + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethUsdc, + 0, // nft id from setIdWethUsdc + 0, // 0 collateral + amtPayback, // - 0.05 debt + new BigNumber(parseUnits("0.05", 6).toString()), + [setIdWethUsdc, '0', '0', '0', '0'], + [setIdWethUsdc, '0', '0', '0', '0'] + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte(parseUnits("10.05", 6)); + }); + + // // 0.96 matic, 10 weth, 10.05 usdc + + it("Should withdraw 100 Matic from Fluid in MATIC-USDC", async function () { + const amt = new BigNumber(parseEther("100").toString()).multipliedBy(-1); // 100 Matic + + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultMaticUsdc, + 0, // nft id from setIdMaticUsdc + amt, // - 100 collateral + 0, // 0 debt + 0, + [setIdMaticUsdc, '0', '0', '0', '0'], + [setIdMaticUsdc, '0', '0', '0', '0'] + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.gte( + parseEther("100") + ); + }); + + // // 100.96 matic, 10 weth, 10.05 usdc + + // it("Should withdraw max Matic from Fluid in MATIC-USDC", async function () { + // const spells = [ + // { + // connector: connectorName, + // method: "operateWithIds", + // args: [ + // vaultMaticUsdc, + // 0, // nft id from setIdMaticUsdc + // ethers.constants.MinInt256, // min integer value + // 0, // 0 debt // 0, - // [0,0,0,0,0], - // [0,0,0,setId3,0] - // ], - // }, - // ]; - - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); - - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( - // parseEther("100") - // ); - // }); - - // // 120 usdc, 100 matic - - // it("Should payback Matic in Fluid", async function () { - // const amtPayback = ethers.BigNumber.from(parseEther("50")).mul(-1); // 50 Matic - - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultWethMatic, - // 0, // nft id from setId3 - // 0, // 0 collateral - // amtPayback, // - 50 debt - // ethers.BigNumber.from(parseEther("50")), - // [0,0,0,0,setId3], - // [0,0,0,0,0] - // ], - // }, - // ]; - - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); - - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( - // ethers.utils.parseEther("50") - // ); - // }); - - // // 120 usdc, 50 matic - - // it("Should payback max Matic in Fluid", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultWethMatic, - // 0, // nft id from setId3 - // 0, // 0 collateral - // ethers.constants.MinInt256, // min Int - // ethers.BigNumber.from(parseEther("50")), - // [0,0,0,0,0], - // [0,0,0,0,0] - // ], - // }, - // ]; - - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); - - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( - // ethers.utils.parseEther("1") - // ); - // }); - - // 120 usdc, 0 matic///////// - - // it("Should payback Usdc in Fluid", async function () { - // const amtPayback = ethers.BigNumber.from(parseUnits("60", 6)).mul(-1); // 60 usdc - - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultMaticUsdc, - // 0, // nft id from setIdWethUsdc - // 0, // 0 collateral - // amtPayback, // - 60 debt - // dsaWallet0.address, - // setIdMaticUsdc, - // 0 - // ], - // }, - // ]; - - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); - - // expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.lte(parseUnits("60", 6)); - // }); - - // // 60 usdc, 0 matic - - // it("Should payback max Matic in Fluid", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultMaticUsdc, - // 0, // nft id from setIdWethUsdc - // 0, // 0 collateral - // ethers.constants.MinInt256, // min Int - // dsaWallet0.address, - // setIdMaticUsdc, - // 0 - // ], - // }, - // ]; - - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); - // await tx.wait(); - - // expect(await usdcToken.balanceOf(dsaWallet0.address)).to.be.lte(parseUnits("1", 6)); - // }); - - // // 0 usdc, 0 matic - - // it("Should withdraw Matic from Fluid", async function () { - // const amt = ethers.BigNumber.from(parseEther("100")).mul(-1); // 100 Matic - - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultMaticUsdc, - // 0, // nft id from setIdMaticUsdc - // amt, // - 100 collateral - // 0, // 0 debt - // dsaWallet0.address, - // setIdMaticUsdc, - // 0 + // [setIdMaticUsdc, '0', '0', '0', '0'], + // ['0', '0', '0', '0', '0'] // ], // }, // ]; @@ -549,103 +637,78 @@ describe("Fluid", function () { // await tx.wait(); // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( - // parseEther("100") + // parseEther("2000") // ); // }); - // // 0 usdc, 100 matic + // // 2000.96 matic, 10 weth, 10.05 usdc - // it("Should withdraw max Matic from Fluid", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultMaticUsdc, - // 0, // nft id from setIdMaticUsdc - // ethers.constants.MinInt256, // min integer value - // 0, // 0 debt - // dsaWallet0.address, - // setIdMaticUsdc, - // 0 - // ], - // }, - // ]; + it("Should withdraw 0.4 WETH from Fluid in WETH-USDC", async function () { + const amt = new BigNumber(parseEther("0.4").toString()).multipliedBy(-1); // 1 Weth - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethUsdc, + 0, // nft id from setIdWethUsdc + amt, // -1 collateral + 0, // 0 debt + 0, + [setIdWethUsdc, '0', '0', '0', '0'], + [setIdWethUsdc, '0', '0', '0', '0'] + ], + }, + ]; - // await tx.wait(); + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( - // parseEther("1000") - // ); - // }); + await tx.wait(); - // // 0 usdc, 1000 matic + // expect(await wethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.gte( + // parseEther("11") + // ); + }); - // it("Should withdraw WETH from Fluid", async function () { - // const amt = ethers.BigNumber.from(parseEther("10")).mul(-1); // 10 Weth + // // 2000.96 matic, 11 weth, 10.05 usdc - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultWethUsdc, - // 0, // nft id from setIdWethUsdc - // amt, // -10 collateral - // 0, // 0 debt - // dsaWallet0.address, - // setIdWethUsdc, - // 0 - // ], - // }, - // ]; + it("Should payback 0.02 and withdraw 0.5 WETH from Fluid in WETH-USDC", async function () { + const amt = new BigNumber(parseEther("0.5").toString()).multipliedBy(-1); // 1 Weth + const paybackAmt = new BigNumber(parseUnits("0.02", 6).toString()).multipliedBy(-1); // 1 Weth - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); + const spells = [ + { + connector: connectorName, + method: "operateWithIds", + args: [ + vaultWethUsdc, + 0, // nft id from setIdWethUsdc + amt, // -1 collateral + paybackAmt, // 0 debt + new BigNumber(parseUnits("0.03", 6).toString()), + [setIdWethUsdc, '0', '0', '0', '0'], + [setIdWethUsdc, '0', '0', '0', '0'] + ], + }, + ]; - // await tx.wait(); + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); - // expect(await wethToken.balanceOf(dsaWallet0.address)).to.eq( - // parseEther("10") - // ); - // }); + await tx.wait(); - // // 0 usdc, 1000 matic, 10 weth + // expect(await wethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.gte( + // parseEther("12") + // ); - // it("Should withdraw max WETH from Fluid", async function () { - // const spells = [ - // { - // connector: connectorName, - // method: "operate", - // args: [ - // vaultWethUsdc, - // 0, // nft id from setIdWethUsdc - // ethers.constants.MinInt256, // min integer value - // 0, // 0 debt - // dsaWallet0.address, - // setIdWethUsdc, - // 0 - // ], - // }, - // ]; - - // const tx = await dsaWallet0 - // .connect(wallet0) - // .cast(...encodeSpells(spells), wallet1.getAddress()); - - // await tx.wait(); - - // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( - // parseEther("29") - // ); - // }); - - // 0 usdc, 1000 matic, 30 weth + // expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.gte( + // parseEther("11") + // ); + }); // todo: add a (payback and withdraw case) });