-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #113 from Instadapp/crv-usd
feat: add crv resolver
- Loading branch information
Showing
4 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// 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 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(CONTROLLER_FACTORY.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(); | ||
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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 fractionPerSecond; | ||
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(address controller) external view returns (uint256); | ||
|
||
function rate() 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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.6; | ||
import "./helpers.sol"; | ||
|
||
/** | ||
*@title Curve USD Resolver | ||
*@dev get user position, user configuration, market configuration. | ||
*/ | ||
contract CurveUSDResolver 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 InstaCurveUSDResolver is CurveUSDResolver { | ||
string public constant name = "CRVUSD-Resolver-v1.0"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 { InstaCurveUSDResolver, InstaCurveUSDResolver__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: InstaCurveUSDResolver; | ||
before(async () => { | ||
const deployer = new InstaCurveUSDResolver__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(`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}`); | ||
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("-----------------------------------------------------------"); | ||
} | ||
}); | ||
}); | ||
}); |