From 560a827eedf225446ed987557accee00f69a0a45 Mon Sep 17 00:00:00 2001 From: q1q0 Date: Thu, 7 Sep 2023 16:11:57 -0400 Subject: [PATCH 1/4] feat: add crv resolver --- .../protocols/mainnet/crv-usd/helpers.sol | 48 ++++++++ .../protocols/mainnet/crv-usd/interfaces.sol | 112 ++++++++++++++++++ contracts/protocols/mainnet/crv-usd/main.sol | 99 ++++++++++++++++ test/mainnet/crv_usd.test.ts | 84 +++++++++++++ 4 files changed, 343 insertions(+) create mode 100644 contracts/protocols/mainnet/crv-usd/helpers.sol create mode 100644 contracts/protocols/mainnet/crv-usd/interfaces.sol create mode 100644 contracts/protocols/mainnet/crv-usd/main.sol create mode 100644 test/mainnet/crv_usd.test.ts diff --git a/contracts/protocols/mainnet/crv-usd/helpers.sol b/contracts/protocols/mainnet/crv-usd/helpers.sol new file mode 100644 index 0000000..57963b8 --- /dev/null +++ b/contracts/protocols/mainnet/crv-usd/helpers.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; +import "./interfaces.sol"; +import { DSMath } from "../../../utils/dsmath.sol"; + +contract CRVHelpers is DSMath { + address internal constant CRV_USD = 0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E; + /** + * @dev ControllerFactory Interface + */ + IControllerFactory internal constant ctrFactory = IControllerFactory(0xC9332fdCB1C491Dcc683bAe86Fe3cb70360738BC); + + /** + * @dev Get controller address by given collateral asset + */ + function getController(address collateral, uint256 i) internal view returns (IController controller) { + controller = IController(ctrFactory.get_controller(collateral, i)); + } + + function getMarketConfig(address market, uint256 index) internal view returns (MarketConfig memory config) { + IController controller = getController(market, index); + address AMM = controller.amm(); + address monetary = controller.monetary_policy(); + config.controller = address(controller); + config.AMM = AMM; + config.monetary = monetary; + config.oraclePrice = controller.amm_price(); + config.loanLen = controller.n_loans(); + config.totalDebt = controller.total_debt(); + + address coin0 = I_LLAMMA(AMM).coins(0); + address coin1 = I_LLAMMA(AMM).coins(1); + uint8 decimals0 = IERC20(coin0).decimals(); + uint8 decimals1 = IERC20(coin1).decimals(); + uint256 amount0 = IERC20(coin0).balanceOf(AMM); + uint256 amount1 = IERC20(coin1).balanceOf(AMM); + + Coins memory c = Coins(coin0, coin1, decimals0, decimals1, amount0, amount1); + + config.coins = c; + config.borrowable = IERC20(CRV_USD).balanceOf(address(controller)); + config.basePrice = I_LLAMMA(AMM).get_base_price(); + config.A = I_LLAMMA(AMM).A(); + config.rate0 = IMonetary(monetary).rate0(); + config.sigma = IMonetary(monetary).sigma(); + config.targetDebtFraction = IMonetary(monetary).target_debt_fraction(); + } +} diff --git a/contracts/protocols/mainnet/crv-usd/interfaces.sol b/contracts/protocols/mainnet/crv-usd/interfaces.sol new file mode 100644 index 0000000..2cb014e --- /dev/null +++ b/contracts/protocols/mainnet/crv-usd/interfaces.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0; + +struct PositionData { + uint256 supply; + uint256 borrow; + uint256 N; + bool existLoan; + uint256 health; + UserPrices prices; + uint256 loanId; +} + +struct UserPrices { + uint256 upper; + uint256 lower; +} + +struct Coins { + address coin0; // borrowToken + address coin1; // collateralToken + uint8 coin0Decimals; + uint8 coin1Decimals; + uint256 coin0Amount; + uint256 coin1Amount; +} + +struct MarketConfig { + uint256 totalDebt; + uint256 basePrice; // internal oracle price + uint256 oraclePrice; // external oracle price + uint256 A; // amplicitation coefficient + uint256 loanLen; + uint256 rate0; + int256 sigma; + uint256 targetDebtFraction; + address controller; + address AMM; + address monetary; + uint256 borrowable; + Coins coins; // factors for total collaterals +} + +interface IControllerFactory { + function get_controller(address collateral, uint256 index) external view returns (address); +} + +interface IController { + function user_state(address user) external view returns (uint256[4] memory); + + function debt(address user) external view returns (uint256); + + function total_debt() external view returns (uint256); + + function loan_exists(address user) external view returns (bool); + + function user_prices(address user) external view returns (uint256[2] memory); + + function health(address user, bool full) external view returns (uint256); + + function n_loans() external view returns (uint256); + + function loan_ix(address user) external view returns (uint256); + + function amm() external view returns (address); + + function loan_discount() external view returns (uint256); + + function liquidation_discount() external view returns (uint256); + + function amm_price() external view returns (uint256); + + function monetary_policy() external view returns (address); +} + +interface I_LLAMMA { + function price_oracle() external view returns (uint256); + + function A() external view returns (uint256); + + function coins(uint256 i) external view returns (address); + + function get_base_price() external view returns (uint256); +} + +interface IMonetary { + function rate() external view returns (uint256); + + function rate0() external view returns (uint256); + + function sigma() external view returns (int256); + + function target_debt_fraction() external view returns (uint256); + + function peg_keepers(uint256 arg0) external view returns (address); +} + +interface IPegKeeper { + function debt() external view returns (uint256); +} + +interface IERC20 { + function balanceOf(address account) external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + function approve(address spender, uint256 amount) external returns (bool); + + function symbol() external view returns (string memory); + + function decimals() external view returns (uint8); +} diff --git a/contracts/protocols/mainnet/crv-usd/main.sol b/contracts/protocols/mainnet/crv-usd/main.sol new file mode 100644 index 0000000..7b9c955 --- /dev/null +++ b/contracts/protocols/mainnet/crv-usd/main.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.6; +import "./helpers.sol"; + +/** + *@title Compund III Resolver + *@dev get user position, user configuration, market configuration. + */ +contract CRVResolver is CRVHelpers { + /** + *@dev get position of the user for given collateral. + *@notice get position details of the user in a market. + *@param user Address of the user whose position details are needed. + *@param market Address of the market for which the user's position details are needed + *@param index This is used for getting controller. + *@return positionData position details of the user - balances, collaterals and flags. + *@return marketConfig the market configuration details. + */ + function getPosition( + address user, + address market, + uint256 index + ) public view returns (PositionData memory positionData, MarketConfig memory marketConfig) { + IController controller = getController(market, index); + uint256[4] memory res = controller.user_state(user); + positionData.borrow = res[2]; + positionData.supply = res[0]; + positionData.N = res[3]; + positionData.existLoan = controller.loan_exists(user); + positionData.health = positionData.existLoan ? controller.health(user, false) : 0; + positionData.loanId = controller.loan_ix(user); + if (positionData.existLoan) { + uint256[2] memory prices = controller.user_prices(user); + UserPrices memory userPrices = UserPrices(prices[0], prices[1]); + positionData.prices = userPrices; + } + + marketConfig = getMarketConfig(market, index); + } + + /** + *@dev get position of the user for given collateral. + *@notice get position details of the user in a market. + *@param user Address of the user whose position details are needed. + *@param markets Addresses of the market for which the user's position details are needed + *@param indexes Array of index. It should be matched with markets. + *@return positionData Array of positions details of the user - balances, collaterals and flags. + *@return marketConfig Array of markets configuration details. + */ + function getPositionAll( + address user, + address[] memory markets, + uint256[] memory indexes + ) public view returns (PositionData[] memory positionData, MarketConfig[] memory marketConfig) { + require(markets.length == indexes.length); + uint256 length = markets.length; + positionData = new PositionData[](length); + marketConfig = new MarketConfig[](length); + for (uint256 i = 0; i < length; i++) { + (positionData[i], marketConfig[i]) = getPosition(user, markets[i], indexes[i]); + } + } + + /** + *@dev get position of the user for all collaterals. + *@notice get position details of the user in a market. + *@param market Address of the market for which the user's position details are needed + *@param index This is used for getting controller. + *@return marketConfig Detailed market configuration. + */ + function getMarketDetails(address market, uint256 index) public view returns (MarketConfig memory marketConfig) { + marketConfig = getMarketConfig(market, index); + } + + /** + *@dev get position of the user for all collaterals. + *@notice get position details of the user in a market. + *@param markets Addresses of the market for which the user's position details are needed + *@param indexes Array of index. It should be matched with markets. + *@return marketConfig Array of detailed market configuration. + */ + function getMarketDetailsAll(address[] memory markets, uint256[] memory indexes) + public + view + returns (MarketConfig[] memory marketConfig) + { + require(markets.length == indexes.length); + uint256 length = markets.length; + marketConfig = new MarketConfig[](length); + + for (uint256 i = 0; i < length; i++) { + marketConfig[i] = getMarketConfig(markets[i], indexes[i]); + } + } +} + +contract InstaCRVResolver is CRVResolver { + string public constant name = "CRV-USD-Resolver-v1.0"; +} diff --git a/test/mainnet/crv_usd.test.ts b/test/mainnet/crv_usd.test.ts new file mode 100644 index 0000000..e5e4b8e --- /dev/null +++ b/test/mainnet/crv_usd.test.ts @@ -0,0 +1,84 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +// import { expect } from "chai"; +// import { formatEther, formatUnits } from "ethers/lib/utils"; +import { ethers } from "hardhat"; +import { InstaCRVResolver, InstaCRVResolver__factory } from "../../typechain"; +// import { Tokens } from "../consts"; + +describe("CRV-USD Resolvers", () => { + let signer: SignerWithAddress; + const user = "0x294125EBE0a93815A68E0165935c521275E2Dc1e"; + const markets = [ + "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + "0xac3e018457b222d93114458476f3e3416abbe38f", //sfrxETH version 1 + "0x18084fba666a33d37592fa2633fd49a74dd93a88", + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "0xac3e018457b222d93114458476f3e3416abbe38f", // sfrxETH version 2 + ]; + const indexed = [ + 0, + 0, + 0, //sfrxETH version 1 + 0, + 0, + 1, // sfrxETH version 2 + ]; + + before(async () => { + [signer] = await ethers.getSigners(); + }); + + describe("CRV-USD Resolver", () => { + let resolver: InstaCRVResolver; + before(async () => { + const deployer = new InstaCRVResolver__factory(signer); + resolver = await deployer.deploy(); + await resolver.deployed(); + }); + + it("Returns the market's configurations", async () => { + const marketConfig = await resolver.getMarketDetailsAll(markets, indexed); + for (const market of marketConfig) { + console.log("======================================================"); + console.log(`Total Debt: ${market.totalDebt}`); + console.log(`basePrice: ${market.basePrice}`); + console.log(`oracle price: ${market.oraclePrice}`); + console.log(`Amplicitation coefficient: ${market.A}`); + console.log(`Count of loan: ${market.loanLen}`); + console.log(`Rate0: ${market.rate0}`); + console.log(`sigma factor: ${market.sigma}`); + console.log(`Fraction of the target fraction: ${market.targetDebtFraction}`); + console.log(`CRV market controller address: ${market.controller}`); + console.log(`AMM address: ${market.AMM}`); + console.log(`Monetary address: ${market.monetary}`); + console.log(`total Curve borrowable amount: ${market.borrowable}`); + console.log(`Coin0 address: ${market.coins.coin0}`); + console.log(`Coin1 address: ${market.coins.coin1}`); + console.log(`Coin0 token decimals: ${market.coins.coin0Decimals}`); + console.log(`Coin1 token decimals: ${market.coins.coin1Decimals}`); + console.log(`Coin0 balance: ${market.coins.coin0Amount}`); + console.log(`Coin1 balance: ${market.coins.coin1Amount}`); + console.log("======================================================"); + } + }); + + it("Returns the user's position details", async () => { + const userPositions = await resolver.getPositionAll(user, markets, indexed); + + for (const position of userPositions.positionData) { + console.log("-----------------------------------------------------------"); + console.log(`**User Position Data:**`); + console.log(`Supplied balance: ${position.supply}`); + console.log(`Borrowed balance: ${position.borrow}`); + console.log(`User band Number: ${position.N}`); + console.log(`Is created loan?: ${position.existLoan}`); + console.log(`User health: ${position.health}`); + console.log(`Use loan ID: ${position.loanId}`); + console.log(`User upper price: ${position.prices.upper}`); + console.log(`User lower price: ${position.prices.lower}`); + console.log("-----------------------------------------------------------"); + } + }); + }); +}); From d437b1e2e81fbee7e3dbc6561339f7514e3b1b2f Mon Sep 17 00:00:00 2001 From: q1q0 Date: Fri, 8 Sep 2023 15:37:17 -0400 Subject: [PATCH 2/4] feat: add fractionPerSecond --- contracts/protocols/mainnet/crv-usd/helpers.sol | 6 +++++- contracts/protocols/mainnet/crv-usd/interfaces.sol | 6 +++--- test/mainnet/crv_usd.test.ts | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/protocols/mainnet/crv-usd/helpers.sol b/contracts/protocols/mainnet/crv-usd/helpers.sol index 57963b8..d12862c 100644 --- a/contracts/protocols/mainnet/crv-usd/helpers.sol +++ b/contracts/protocols/mainnet/crv-usd/helpers.sol @@ -41,7 +41,11 @@ contract CRVHelpers is DSMath { config.borrowable = IERC20(CRV_USD).balanceOf(address(controller)); config.basePrice = I_LLAMMA(AMM).get_base_price(); config.A = I_LLAMMA(AMM).A(); - config.rate0 = IMonetary(monetary).rate0(); + try IMonetary(monetary).rate(address(controller)) returns (uint256 rate) { + config.fractionPerSecond = rate; + } catch { + config.fractionPerSecond = IMonetary(monetary).rate(); + } config.sigma = IMonetary(monetary).sigma(); config.targetDebtFraction = IMonetary(monetary).target_debt_fraction(); } diff --git a/contracts/protocols/mainnet/crv-usd/interfaces.sol b/contracts/protocols/mainnet/crv-usd/interfaces.sol index 2cb014e..e5e3bb5 100644 --- a/contracts/protocols/mainnet/crv-usd/interfaces.sol +++ b/contracts/protocols/mainnet/crv-usd/interfaces.sol @@ -31,7 +31,7 @@ struct MarketConfig { uint256 oraclePrice; // external oracle price uint256 A; // amplicitation coefficient uint256 loanLen; - uint256 rate0; + uint256 fractionPerSecond; int256 sigma; uint256 targetDebtFraction; address controller; @@ -84,9 +84,9 @@ interface I_LLAMMA { } interface IMonetary { - function rate() external view returns (uint256); + function rate(address controller) external view returns (uint256); - function rate0() external view returns (uint256); + function rate() external view returns (uint256); function sigma() external view returns (int256); diff --git a/test/mainnet/crv_usd.test.ts b/test/mainnet/crv_usd.test.ts index e5e4b8e..cfcdc38 100644 --- a/test/mainnet/crv_usd.test.ts +++ b/test/mainnet/crv_usd.test.ts @@ -46,7 +46,7 @@ describe("CRV-USD Resolvers", () => { console.log(`oracle price: ${market.oraclePrice}`); console.log(`Amplicitation coefficient: ${market.A}`); console.log(`Count of loan: ${market.loanLen}`); - console.log(`Rate0: ${market.rate0}`); + console.log(`fractionPerSecond: ${market.fractionPerSecond}`); console.log(`sigma factor: ${market.sigma}`); console.log(`Fraction of the target fraction: ${market.targetDebtFraction}`); console.log(`CRV market controller address: ${market.controller}`); From 0ceead7edbf4aacf3c75908d5eae454664872b23 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Wed, 13 Sep 2023 21:32:32 +0400 Subject: [PATCH 3/4] feat: minor updates --- contracts/protocols/mainnet/crv-usd/helpers.sol | 5 +++-- contracts/protocols/mainnet/crv-usd/main.sol | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/contracts/protocols/mainnet/crv-usd/helpers.sol b/contracts/protocols/mainnet/crv-usd/helpers.sol index d12862c..8bc4772 100644 --- a/contracts/protocols/mainnet/crv-usd/helpers.sol +++ b/contracts/protocols/mainnet/crv-usd/helpers.sol @@ -8,13 +8,14 @@ contract CRVHelpers is DSMath { /** * @dev ControllerFactory Interface */ - IControllerFactory internal constant ctrFactory = IControllerFactory(0xC9332fdCB1C491Dcc683bAe86Fe3cb70360738BC); + IControllerFactory internal constant CONTROLLER_FACTORY = + IControllerFactory(0xC9332fdCB1C491Dcc683bAe86Fe3cb70360738BC); /** * @dev Get controller address by given collateral asset */ function getController(address collateral, uint256 i) internal view returns (IController controller) { - controller = IController(ctrFactory.get_controller(collateral, i)); + controller = IController(CONTROLLER_FACTORY.get_controller(collateral, i)); } function getMarketConfig(address market, uint256 index) internal view returns (MarketConfig memory config) { diff --git a/contracts/protocols/mainnet/crv-usd/main.sol b/contracts/protocols/mainnet/crv-usd/main.sol index 7b9c955..b90f3bc 100644 --- a/contracts/protocols/mainnet/crv-usd/main.sol +++ b/contracts/protocols/mainnet/crv-usd/main.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.6; import "./helpers.sol"; /** - *@title Compund III Resolver + *@title Curve USD Resolver *@dev get user position, user configuration, market configuration. */ -contract CRVResolver is CRVHelpers { +contract CurveUSDResolver is CRVHelpers { /** *@dev get position of the user for given collateral. *@notice get position details of the user in a market. @@ -94,6 +94,6 @@ contract CRVResolver is CRVHelpers { } } -contract InstaCRVResolver is CRVResolver { - string public constant name = "CRV-USD-Resolver-v1.0"; +contract InstaCurveUSDResolver is CurveUSDResolver { + string public constant name = "CRVUSD-Resolver-v1.0"; } From aec1bb50fa1700a000d3897c124ab9f70572adc7 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Wed, 13 Sep 2023 21:37:27 +0400 Subject: [PATCH 4/4] feat: rename folder name --- contracts/protocols/mainnet/{crv-usd => crvusd}/helpers.sol | 0 .../protocols/mainnet/{crv-usd => crvusd}/interfaces.sol | 0 contracts/protocols/mainnet/{crv-usd => crvusd}/main.sol | 0 test/mainnet/crv_usd.test.ts | 6 +++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename contracts/protocols/mainnet/{crv-usd => crvusd}/helpers.sol (100%) rename contracts/protocols/mainnet/{crv-usd => crvusd}/interfaces.sol (100%) rename contracts/protocols/mainnet/{crv-usd => crvusd}/main.sol (100%) diff --git a/contracts/protocols/mainnet/crv-usd/helpers.sol b/contracts/protocols/mainnet/crvusd/helpers.sol similarity index 100% rename from contracts/protocols/mainnet/crv-usd/helpers.sol rename to contracts/protocols/mainnet/crvusd/helpers.sol diff --git a/contracts/protocols/mainnet/crv-usd/interfaces.sol b/contracts/protocols/mainnet/crvusd/interfaces.sol similarity index 100% rename from contracts/protocols/mainnet/crv-usd/interfaces.sol rename to contracts/protocols/mainnet/crvusd/interfaces.sol diff --git a/contracts/protocols/mainnet/crv-usd/main.sol b/contracts/protocols/mainnet/crvusd/main.sol similarity index 100% rename from contracts/protocols/mainnet/crv-usd/main.sol rename to contracts/protocols/mainnet/crvusd/main.sol diff --git a/test/mainnet/crv_usd.test.ts b/test/mainnet/crv_usd.test.ts index cfcdc38..7b4d7dc 100644 --- a/test/mainnet/crv_usd.test.ts +++ b/test/mainnet/crv_usd.test.ts @@ -2,7 +2,7 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; // import { expect } from "chai"; // import { formatEther, formatUnits } from "ethers/lib/utils"; import { ethers } from "hardhat"; -import { InstaCRVResolver, InstaCRVResolver__factory } from "../../typechain"; +import { InstaCurveUSDResolver, InstaCurveUSDResolver__factory } from "../../typechain"; // import { Tokens } from "../consts"; describe("CRV-USD Resolvers", () => { @@ -30,9 +30,9 @@ describe("CRV-USD Resolvers", () => { }); describe("CRV-USD Resolver", () => { - let resolver: InstaCRVResolver; + let resolver: InstaCurveUSDResolver; before(async () => { - const deployer = new InstaCRVResolver__factory(signer); + const deployer = new InstaCurveUSDResolver__factory(signer); resolver = await deployer.deploy(); await resolver.deployed(); });