Skip to content

Commit

Permalink
Merge pull request #113 from Instadapp/crv-usd
Browse files Browse the repository at this point in the history
feat: add crv resolver
  • Loading branch information
shriyatyagii authored Sep 13, 2023
2 parents 04c9010 + aec1bb5 commit 90e1d4e
Show file tree
Hide file tree
Showing 4 changed files with 348 additions and 0 deletions.
53 changes: 53 additions & 0 deletions contracts/protocols/mainnet/crvusd/helpers.sol
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();
}
}
112 changes: 112 additions & 0 deletions contracts/protocols/mainnet/crvusd/interfaces.sol
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);
}
99 changes: 99 additions & 0 deletions contracts/protocols/mainnet/crvusd/main.sol
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";
}
84 changes: 84 additions & 0 deletions test/mainnet/crv_usd.test.ts
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("-----------------------------------------------------------");
}
});
});
});

0 comments on commit 90e1d4e

Please sign in to comment.