From 24ffd46db70345ec2e1face9b6cfd6daf8db392e Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sun, 24 Dec 2023 15:51:25 +0400 Subject: [PATCH] feat: add fluid connector --- contracts/mainnet/connectors/fluid/events.sol | 14 ++ .../mainnet/connectors/fluid/interface.sol | 47 +++++ contracts/mainnet/connectors/fluid/main.sol | 98 ++++++++++ package-lock.json | 168 +++++++++++++++++- package.json | 3 + 5 files changed, 322 insertions(+), 8 deletions(-) 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..fb49026 --- /dev/null +++ b/contracts/mainnet/connectors/fluid/events.sol @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.2; + +contract Events { + event LogOperate ( + address vaultAddress, + uint256 nftId, + int256 newCol, + int256 newDebt, + address to, + uint256 getId, + uint256 setId + ); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/fluid/interface.sol b/contracts/mainnet/connectors/fluid/interface.sol new file mode 100644 index 0000000..f955ded --- /dev/null +++ b/contracts/mainnet/connectors/fluid/interface.sol @@ -0,0 +1,47 @@ +//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_); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/fluid/main.sol b/contracts/mainnet/connectors/fluid/main.sol new file mode 100644 index 0000000..db16988 --- /dev/null +++ b/contracts/mainnet/connectors/fluid/main.sol @@ -0,0 +1,98 @@ +//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 + * @param newDebt_ New debt. If positive then borrow, if negative then payback, if 0 then do nothing + * @param to_ Address where withdraw or borrow should go. If address(0) then msg.sender + * @param getId_ ID to retrieve NFT ID. + * @param setId_ ID stores the NFT ID generated. + */ + function operate( + address vaultAddress_, + uint256 nftId_, + int256 newCol_, + int256 newDebt_, + address to_, + uint256 getId_, + uint256 setId_ + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + nftId_ = getUint(getId_, nftId_); + + IVault vault_ = IVault(vaultAddress_); + + IVault.ConstantViews memory vaultDetails_ = vault_.constantsView(); + + bool isSupplyTokenEth_ = vaultDetails_.supplyToken == getEthAddr(); + bool isBorrowTokenEth_ = vaultDetails_.borrowToken == getEthAddr(); + + if (newCol_ > 0 && !isSupplyTokenEth_) { + TokenInterface(vaultDetails_.supplyToken).approve( + vaultAddress_, + uint256(newCol_) + ); + } + + if (newDebt_ < 0 && !isBorrowTokenEth_) { + TokenInterface(vaultDetails_.borrowToken).approve( + vaultAddress_, + uint256(-1 * newDebt_) + ); + } + + uint256 colEthAmount_ = isSupplyTokenEth_ && newCol_ > 0 + ? uint256(newCol_) + : 0; + + uint256 debtEthAmount_ = isBorrowTokenEth_ && newDebt_ < 0 + ? uint256(-1 * newDebt_) + : 0; + + (nftId_, newCol_, newDebt_) = vault_.operate{ + value: colEthAmount_ + debtEthAmount_ + }(nftId_, newCol_, newDebt_, to_); + + setUint(setId_, nftId_); + + _eventName = "LogOperate(address,uint256,int256,int256,address,uint256,uint256)"; + _eventParam = abi.encode( + vaultAddress_, + nftId_, + newCol_, + newDebt_, + to_, + getId_, + setId_ + ); + } +} + +contract ConnectV2Fluid is FluidConnector { + string public constant name = "Fluid-v1.0"; +} diff --git a/package-lock.json b/package-lock.json index 565759d..561a3c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,11 +8,14 @@ "name": "dsa-connectors-2.0", "version": "1.0.0", "dependencies": { + "@nomiclabs/hardhat-ethers": "^2.2.3", "@openzeppelin/contracts": "^4.9.5", "@typechain/ethers-v5": "^10.2.1", "@typechain/hardhat": "^6.1.6", "bignumber.js": "^4.0.4", "dotenv": "^16.3.1", + "prettier": "^3.1.1", + "prettier-plugin-solidity": "^1.2.0", "ts-node": "^10.9.2", "web3": "^4.3.0" }, @@ -1390,6 +1393,15 @@ "node": ">= 10" } }, + "node_modules/@nomiclabs/hardhat-ethers": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz", + "integrity": "sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==", + "peerDependencies": { + "ethers": "^5.0.0", + "hardhat": "^2.0.0" + } + }, "node_modules/@openzeppelin/contracts": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.5.tgz", @@ -1529,6 +1541,14 @@ "node": ">=6" } }, + "node_modules/@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -1800,6 +1820,11 @@ "node": ">=4" } }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -3839,19 +3864,65 @@ } }, "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", "bin": { - "prettier": "bin-prettier.js" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=10.13.0" + "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-solidity": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.2.0.tgz", + "integrity": "sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA==", + "dependencies": { + "@solidity-parser/parser": "^0.16.2", + "semver": "^7.5.4", + "solidity-comments-extractor": "^0.0.7" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "prettier": ">=2.3.0" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prettier-plugin-solidity/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4141,6 +4212,11 @@ "semver": "bin/semver" } }, + "node_modules/solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==" + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4549,6 +4625,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/typechain/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -6232,6 +6322,12 @@ "integrity": "sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==", "optional": true }, + "@nomiclabs/hardhat-ethers": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.3.tgz", + "integrity": "sha512-YhzPdzb612X591FOe68q+qXVXGG2ANZRvDo0RRUtimev85rCrAlv/TLMEZw5c+kq9AbzocLTVX/h2jVIFPL9Xg==", + "requires": {} + }, "@openzeppelin/contracts": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.5.tgz", @@ -6335,6 +6431,14 @@ "tslib": "^1.9.3" } }, + "@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "requires": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, "@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -6541,6 +6645,11 @@ "color-convert": "^1.9.0" } }, + "antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==" + }, "anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -8013,9 +8122,42 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==" + }, + "prettier-plugin-solidity": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.2.0.tgz", + "integrity": "sha512-fgxcUZpVAP+LlRfy5JI5oaAkXGkmsje2VJ5krv/YMm+rcTZbIUwFguSw5f+WFuttMjpDm6wB4UL7WVkArEfiVA==", + "requires": { + "@solidity-parser/parser": "^0.16.2", + "semver": "^7.5.4", + "solidity-comments-extractor": "^0.0.7" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } }, "queue-microtask": { "version": "1.2.3", @@ -8231,6 +8373,11 @@ } } }, + "solidity-comments-extractor": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.7.tgz", + "integrity": "sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw==" + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8516,6 +8663,11 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" } + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==" } } }, diff --git a/package.json b/package.json index 50c6697..62ef349 100644 --- a/package.json +++ b/package.json @@ -8,11 +8,14 @@ "typechain": "^8.3.2" }, "dependencies": { + "@nomiclabs/hardhat-ethers": "^2.2.3", "@openzeppelin/contracts": "^4.9.5", "@typechain/ethers-v5": "^10.2.1", "@typechain/hardhat": "^6.1.6", "bignumber.js": "^4.0.4", "dotenv": "^16.3.1", + "prettier": "^3.1.1", + "prettier-plugin-solidity": "^1.2.0", "ts-node": "^10.9.2", "web3": "^4.3.0" }