Skip to content

Commit afbe8a4

Browse files
authored
Add Compound Lens (#29)
* Add Compound Lens The Compound Lens is a view contract Compound smart contracts that provides the data dApps need to stay hydrated. Specifically, we wrap the main data that our dApp is asking for. There's probably more bundling we could do, but this already reduces the raw number of calls by 98% (from about 10/s to about 0.2/s). * Add CompoundLens deployments
1 parent 86c0116 commit afbe8a4

7 files changed

Lines changed: 195 additions & 5 deletions

File tree

contracts/Lens/CompoundLens.sol

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
pragma solidity ^0.5.16;
2+
pragma experimental ABIEncoderV2;
3+
4+
import "../CErc20.sol";
5+
import "../CToken.sol";
6+
import "../Comptroller.sol";
7+
import "../EIP20Interface.sol";
8+
9+
contract CompoundLens {
10+
struct CTokenMetadata {
11+
address cToken;
12+
uint exchangeRateCurrent;
13+
uint supplyRatePerBlock;
14+
uint borrowRatePerBlock;
15+
uint reserveFactorMantissa;
16+
uint totalBorrows;
17+
uint totalReserves;
18+
uint totalSupply;
19+
uint totalCash;
20+
bool isListed;
21+
uint collateralFactorMantissa;
22+
address underlyingAssetAddress;
23+
uint cTokenDecimals;
24+
uint underlyingDecimals;
25+
}
26+
27+
function cTokenMetadata(CToken cToken) public returns (CTokenMetadata memory) {
28+
uint exchangeRateCurrent = cToken.exchangeRateCurrent();
29+
Comptroller comptroller = Comptroller(address(cToken.comptroller()));
30+
(bool isListed, uint collateralFactorMantissa) = comptroller.markets(address(cToken));
31+
address underlyingAssetAddress;
32+
uint underlyingDecimals;
33+
34+
if (compareStrings(cToken.symbol(), "cETH")) {
35+
underlyingAssetAddress = address(0);
36+
underlyingDecimals = 18;
37+
} else {
38+
CErc20 cErc20 = CErc20(address(cToken));
39+
underlyingAssetAddress = cErc20.underlying();
40+
underlyingDecimals = EIP20Interface(cErc20.underlying()).decimals();
41+
}
42+
43+
return CTokenMetadata({
44+
cToken: address(cToken),
45+
exchangeRateCurrent: exchangeRateCurrent,
46+
supplyRatePerBlock: cToken.supplyRatePerBlock(),
47+
borrowRatePerBlock: cToken.borrowRatePerBlock(),
48+
reserveFactorMantissa: cToken.reserveFactorMantissa(),
49+
totalBorrows: cToken.totalBorrows(),
50+
totalReserves: cToken.totalReserves(),
51+
totalSupply: cToken.totalSupply(),
52+
totalCash: cToken.getCash(),
53+
isListed: isListed,
54+
collateralFactorMantissa: collateralFactorMantissa,
55+
underlyingAssetAddress: underlyingAssetAddress,
56+
cTokenDecimals: cToken.decimals(),
57+
underlyingDecimals: underlyingDecimals
58+
});
59+
}
60+
61+
function cTokenMetadataAll(CToken[] calldata cTokens) external returns (CTokenMetadata[] memory) {
62+
uint cTokenCount = cTokens.length;
63+
CTokenMetadata[] memory res = new CTokenMetadata[](cTokenCount);
64+
for (uint i = 0; i < cTokenCount; i++) {
65+
res[i] = cTokenMetadata(cTokens[i]);
66+
}
67+
return res;
68+
}
69+
70+
struct CTokenBalances {
71+
address cToken;
72+
uint balanceOf;
73+
uint borrowBalanceCurrent;
74+
uint balanceOfUnderlying;
75+
uint tokenBalance;
76+
uint tokenAllowance;
77+
}
78+
79+
function cTokenBalances(CToken cToken, address payable account) public returns (CTokenBalances memory) {
80+
uint balanceOf = cToken.balanceOf(account);
81+
uint borrowBalanceCurrent = cToken.borrowBalanceCurrent(account);
82+
uint balanceOfUnderlying = cToken.balanceOfUnderlying(account);
83+
uint tokenBalance;
84+
uint tokenAllowance;
85+
86+
if (compareStrings(cToken.symbol(), "cETH")) {
87+
tokenBalance = account.balance;
88+
tokenAllowance = account.balance;
89+
} else {
90+
CErc20 cErc20 = CErc20(address(cToken));
91+
EIP20Interface underlying = EIP20Interface(cErc20.underlying());
92+
tokenBalance = underlying.balanceOf(account);
93+
tokenAllowance = underlying.allowance(account, address(cToken));
94+
}
95+
96+
return CTokenBalances({
97+
cToken: address(cToken),
98+
balanceOf: balanceOf,
99+
borrowBalanceCurrent: borrowBalanceCurrent,
100+
balanceOfUnderlying: balanceOfUnderlying,
101+
tokenBalance: tokenBalance,
102+
tokenAllowance: tokenAllowance
103+
});
104+
}
105+
106+
function cTokenBalancesAll(CToken[] calldata cTokens, address payable account) external returns (CTokenBalances[] memory) {
107+
uint cTokenCount = cTokens.length;
108+
CTokenBalances[] memory res = new CTokenBalances[](cTokenCount);
109+
for (uint i = 0; i < cTokenCount; i++) {
110+
res[i] = cTokenBalances(cTokens[i], account);
111+
}
112+
return res;
113+
}
114+
115+
struct CTokenUnderlyingPrice {
116+
address cToken;
117+
uint underlyingPrice;
118+
}
119+
120+
function cTokenUnderlyingPrice(CToken cToken) public returns (CTokenUnderlyingPrice memory) {
121+
Comptroller comptroller = Comptroller(address(cToken.comptroller()));
122+
PriceOracle priceOracle = comptroller.oracle();
123+
124+
return CTokenUnderlyingPrice({
125+
cToken: address(cToken),
126+
underlyingPrice: priceOracle.getUnderlyingPrice(cToken)
127+
});
128+
}
129+
130+
function cTokenUnderlyingPriceAll(CToken[] calldata cTokens) external returns (CTokenUnderlyingPrice[] memory) {
131+
uint cTokenCount = cTokens.length;
132+
CTokenUnderlyingPrice[] memory res = new CTokenUnderlyingPrice[](cTokenCount);
133+
for (uint i = 0; i < cTokenCount; i++) {
134+
res[i] = cTokenUnderlyingPrice(cTokens[i]);
135+
}
136+
return res;
137+
}
138+
139+
struct AccountLimits {
140+
CToken[] markets;
141+
uint liquidity;
142+
uint shortfall;
143+
}
144+
145+
function getAccountLimits(Comptroller comptroller, address account) public returns (AccountLimits memory) {
146+
(uint errorCode, uint liquidity, uint shortfall) = comptroller.getAccountLiquidity(account);
147+
require(errorCode == 0);
148+
149+
return AccountLimits({
150+
markets: comptroller.getAssetsIn(account),
151+
liquidity: liquidity,
152+
shortfall: shortfall
153+
});
154+
}
155+
156+
function compareStrings(string memory a, string memory b) internal pure returns (bool) {
157+
return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b))));
158+
}
159+
}

networks/goerli.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"REP": "0x183Faf58c4461972765f3F90c6272A4ecE66Bd96",
3131
"cZRX": "0xA253295eC2157B8b69C44b2cb35360016DAa25b1",
3232
"cWBTC": "0x6CE27497A64fFFb5517AA4aeE908b1E7EB63B9fF",
33-
"USDC": "0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C"
33+
"USDC": "0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C",
34+
"CompoundLens": "0x05752E558f7141C4b098D9b9B212F4Ea5aff0A3c"
3435
},
3536
"Blocks": {
3637
"ZRX": 1971943,

networks/kovan.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
"cZRX": "0xC014DC10A57aC78350C5fddB26Bb66f1Cb0960a0",
3030
"cWBTC": "0x3659728876EfB2780f498Ce829C5b076e496E0e3",
3131
"USDC": "0x75B0622Cec14130172EaE9Cf166B92E5C112FaFF",
32-
"Base200bps_Slope222bps_Kink90_Jump10": "0xE057EF4be5c6C712e13E24d4408f5F257fEd4d3f"
32+
"Base200bps_Slope222bps_Kink90_Jump10": "0xE057EF4be5c6C712e13E24d4408f5F257fEd4d3f",
33+
"CompoundLens": "0x6F4853929e4C30Fcd357f36d8d1aa9b5fAC82185"
3334
},
3435
"Blocks": {
3536
"ZRX": 14981231,

networks/mainnet.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
"REP": "0x1985365e9f78359a9B6AD760e32412f4a445E862",
3737
"cZRX": "0xB3319f5D18Bc0D84dD1b4825Dcde5d5f7266d407",
3838
"cWBTC": "0xC11b1268C1A384e55C48c2391d8d480264A3A7F4",
39-
"USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
39+
"USDC": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
40+
"CompoundLens": "0xBaaD2b744336D6E4E67Fae2e071C3005b8C17102"
4041
},
4142
"Blocks": {
4243
"cUSDC": 7710760,

networks/rinkeby.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
"REP": "0x6e894660985207feb7cf89Faf048998c71E8EE89",
2525
"cZRX": "0x52201ff1720134bBbBB2f6BC97Bf3715490EC19B",
2626
"cWBTC": "0x0014F450B8Ae7708593F4A46F8fa6E5D50620F96",
27-
"USDC": "0x4DBCdF9B62e891a7cec5A2568C3F4FAF9E8Abe2b"
27+
"USDC": "0x4DBCdF9B62e891a7cec5A2568C3F4FAF9E8Abe2b",
28+
"CompoundLens": "0x0E01460f00C6B0b66A44D7cF1406Cb2150B718fC"
2829
},
2930
"Blocks": {
3031
"cUSDC": 4319847,

networks/ropsten.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"cZRX": "0x3A728dD027AD6F76Cdea227d5Cf5ba7ce9390A3d",
3131
"cWBTC": "0x4D15eE7DE1f86248c986f5AE7dCE855b1c1A8806",
3232
"TBTC": "0x083f652051b9CdBf65735f98d83cc329725Aa957",
33-
"USDC": "0x8a9447df1FB47209D36204e6D56767a33bf20f9f"
33+
"USDC": "0x8a9447df1FB47209D36204e6D56767a33bf20f9f",
34+
"CompoundLens": "0x1496C81e6DE4928bE5E4e9F83dA575A08B616Ac8"
3435
},
3536
"Blocks": {
3637
"ZRX": 5970747,

saddle.config.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,32 @@ module.exports = {
156156
{unlocked: 0}
157157
]
158158
},
159+
kovan: {
160+
providers: [
161+
{env: "PROVIDER"},
162+
{file: "~/.ethereum/kovan-url"}, // Load from given file with contents as the URL (e.g. https://infura.io/api-key)
163+
{http: "https://kovan-eth.compound.finance"}
164+
],
165+
web3: {
166+
gas: [
167+
{env: "GAS"},
168+
{default: "4600000"}
169+
],
170+
gas_price: [
171+
{env: "GAS_PRICE"},
172+
{default: "12000000000"}
173+
],
174+
options: {
175+
transactionConfirmationBlocks: 1,
176+
transactionBlockTimeout: 5
177+
}
178+
},
179+
accounts: [
180+
{env: "ACCOUNT"},
181+
{file: "~/.ethereum/kovan"}, // Load from given file with contents as the private key (e.g. 0x...)
182+
{unlocked: 0}
183+
]
184+
},
159185
mainnet: {
160186
providers: [
161187
{env: "PROVIDER"},

0 commit comments

Comments
 (0)