Skip to content

Commit

Permalink
Merge pull request #121 from 1inch/feature/compound-v3
Browse files Browse the repository at this point in the history
[SC-1083] CompoundV3 Wrapper
  • Loading branch information
zZoMROT authored Mar 12, 2024
2 parents c11bf5b + efd795b commit 41a6ee3
Show file tree
Hide file tree
Showing 10 changed files with 1,378 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ If no direct liquidity pair exists between two tokens, the spot price aggregator
* AaveV2 - [0x06cC74503B6d1eB6D4d6Bc402f48fC07b804105f](https://etherscan.io/address/0x06cC74503B6d1eB6D4d6Bc402f48fC07b804105f)
* AaveV3 - [0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573](https://etherscan.io/address/0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573)
* Compound - [0x7C327E1Ee66d4cF7F4053387241351FDc95A0c04](https://etherscan.io/address/0x7C327E1Ee66d4cF7F4053387241351FDc95A0c04)
* CompoundV3 - [0xd24222B521337DABE4f1e56d351818fbf26905eD](https://etherscan.io/address/0xd24222B521337DABE4f1e56d351818fbf26905eD)
* YVault - [0x9FF110f132d988bfa9bC6a21851Da1aF3aC6EaF8](https://etherscan.io/address/0x9FF110f132d988bfa9bC6a21851Da1aF3aC6EaF8)
* stETH - [0x26daCf7E879b18FE658326ddD3ABC0D6910B3E9F](https://etherscan.io/address/0x26daCf7E879b18FE658326ddD3ABC0D6910B3E9F)
* wstETH - [0x37eB78fE793E89353e46AEe73E299985C3B8d334](https://etherscan.io/address/0x37eB78fE793E89353e46AEe73E299985C3B8d334)
Expand Down Expand Up @@ -145,6 +146,7 @@ If no direct liquidity pair exists between two tokens, the spot price aggregator
* WMATIC - [0xA0446D8804611944F1B527eCD37d7dcbE442caba](https://polygonscan.com/address/0xA0446D8804611944F1B527eCD37d7dcbE442caba)
* AaveV2 - [0x138CE40d675F9a23E4D6127A8600308Cf7A93381](https://polygonscan.com/address/0x138CE40d675F9a23E4D6127A8600308Cf7A93381)
* AaveV3 - [0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573](https://polygonscan.com/address/0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573)
* CompoundV3 - [0xAc63D130525c251EbB24E010c2959a98c80B993a](https://polygonscan.com/address/0xAc63D130525c251EbB24E010c2959a98c80B993a)

</details>

Expand Down Expand Up @@ -222,6 +224,7 @@ If no direct liquidity pair exists between two tokens, the spot price aggregator

* WETH - [0x0F85A912448279111694F4Ba4F85dC641c54b594](https://arbiscan.io/address/0x0F85A912448279111694F4Ba4F85dC641c54b594)
* AaveV3 - [0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573](https://arbiscan.io/address/0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573)
* CompoundV3 - [0x04098C93b15E5Cbb5A49651f20218C85F202Cd27](https://arbiscan.io/address/0x04098C93b15E5Cbb5A49651f20218C85F202Cd27)

</details>

Expand Down Expand Up @@ -488,6 +491,7 @@ If no direct liquidity pair exists between two tokens, the spot price aggregator

* WETH - [0x3Ce81621e674Db129033548CbB9FF31AEDCc1BF6](https://basescan.org/address/0x3Ce81621e674Db129033548CbB9FF31AEDCc1BF6)
* AaveV3 - [0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573](https://basescan.org/address/0x0c8fc7a71C28c768FDC1f7d75835229beBEB1573)
* CompoundV3 - [0x3afA12cf9Ac1a96845973BD93dBEa183A94DD74F](https://basescan.org/address/0x3afA12cf9Ac1a96845973BD93dBEa183A94DD74F)

</details>

Expand Down
10 changes: 10 additions & 0 deletions contracts/interfaces/IComet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

// CompoundV3 money market interface
interface IComet {
function baseToken() external view returns (address);
function supply(address asset, uint amount) external;
function withdraw(address asset, uint amount) external;
}
42 changes: 42 additions & 0 deletions contracts/wrappers/CompoundV3Wrapper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

import "../interfaces/IWrapper.sol";
import "../interfaces/IComet.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract CompoundV3Wrapper is IWrapper, Ownable {
mapping(IERC20 => IERC20) public cTokenToToken;
mapping(IERC20 => IERC20) public tokenTocToken;

constructor(address _owner) Ownable(_owner) {} // solhint-disable-line no-empty-blocks

function addMarkets(address[] memory tokens) external onlyOwner {
for (uint256 i = 0; i < tokens.length; i++) {
IERC20 baseToken = IERC20(IComet(tokens[i]).baseToken());
cTokenToToken[IERC20(tokens[i])] = baseToken;
tokenTocToken[baseToken] = IERC20(tokens[i]);
}
}

function removeMarkets(address[] memory tokens) external onlyOwner {
for (uint256 i = 0; i < tokens.length; i++) {
IERC20 baseToken = IERC20(IComet(tokens[i]).baseToken());
delete cTokenToToken[IERC20(tokens[i])];
delete tokenTocToken[baseToken];
}
}

function wrap(IERC20 token) external view override returns (IERC20 wrappedToken, uint256 rate) {
IERC20 baseToken = cTokenToToken[token];
IERC20 cToken = tokenTocToken[token];
if (baseToken != IERC20(address(0))) {
return (baseToken, 1e18);
} else if (cToken != IERC20(address(0))) {
return (cToken, 1e18);
} else {
revert NotSupportedToken();
}
}
}
311 changes: 311 additions & 0 deletions deployments/arbitrum/CompoundV3Wrapper.json

Large diffs are not rendered by default.

311 changes: 311 additions & 0 deletions deployments/base/CompoundV3Wrapper.json

Large diffs are not rendered by default.

311 changes: 311 additions & 0 deletions deployments/mainnet/CompoundV3Wrapper.json

Large diffs are not rendered by default.

326 changes: 326 additions & 0 deletions deployments/matic/CompoundV3Wrapper.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ module.exports = {
dependencyCompiler: {
paths: [
'@1inch/solidity-utils/contracts/interfaces/ICreate3Deployer.sol',
'@1inch/solidity-utils/contracts/interfaces/IWETH.sol',
],
},
zksolc: {
Expand Down
2 changes: 2 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ const tokens = {
aWETHV3: '0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8',
cDAI: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643',
cETH: '0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5',
cUSDCv3: '0xc3d688B66703497DAA19211EEdff47f25384cdc3',
cWETHv3: '0xA17581A9E3356d9A858b789D68B4d866e593aE94',
iETH: '0xB983E01458529665007fF7E0CDdeCDB74B967Eb6',
iDAI: '0x6b093998D36f2C7F0cc359441FBB24CC629D5FF0',
iUSDC: '0xF013406A0B1d544238083DF0B93ad0d2cBE0f65f',
Expand Down
60 changes: 60 additions & 0 deletions test/wrappers/CompoundV3Wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
const hre = require('hardhat');
const { ethers } = hre;
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { expect, ether, deployContract, trackReceivedTokenAndTx, assertRoughlyEqualValues, constants } = require('@1inch/solidity-utils');
const { tokens } = require('../helpers.js');

describe('CompoundV3Wrapper', function () {
async function initContracts () {
const [wallet, nonOwner] = await ethers.getSigners();
const compoundV3Wrapper = await deployContract('CompoundV3Wrapper', [wallet]);

const IERC20ABI = (await hre.artifacts.readArtifact('IERC20')).abi;
const COMETABI = (await hre.artifacts.readArtifact('IComet')).abi;
const cWETHv3 = new ethers.Contract(tokens.cWETHv3, [...IERC20ABI, ...COMETABI], wallet);
const weth = await ethers.getContractAt('IWETH', tokens.WETH);

await compoundV3Wrapper.addMarkets([tokens.cWETHv3]);
await weth.deposit({ value: ether('2') });
await weth.approve(cWETHv3, ether('2'));

return { wallet, nonOwner, compoundV3Wrapper, weth, cWETHv3 };
}

it('should revert when add/remove by non-owner', async function () {
const { nonOwner, compoundV3Wrapper } = await loadFixture(initContracts);
await expect(compoundV3Wrapper.connect(nonOwner).addMarkets([tokens.cUSDCv3])).to.be.revertedWithCustomError(compoundV3Wrapper, 'OwnableUnauthorizedAccount');
await expect(compoundV3Wrapper.connect(nonOwner).removeMarkets([tokens.cWETHv3])).to.be.revertedWithCustomError(compoundV3Wrapper, 'OwnableUnauthorizedAccount');
});

it('addMarkets', async function () {
const { compoundV3Wrapper } = await loadFixture(initContracts);
await compoundV3Wrapper.addMarkets([tokens.cUSDCv3]);
expect(await compoundV3Wrapper.cTokenToToken(tokens.cUSDCv3)).to.equal(tokens.USDC);
expect(await compoundV3Wrapper.tokenTocToken(tokens.USDC)).to.equal(tokens.cUSDCv3);
});

it('removeMarkets', async function () {
const { compoundV3Wrapper } = await loadFixture(initContracts);
await compoundV3Wrapper.removeMarkets([tokens.cWETHv3]);
expect(await compoundV3Wrapper.cTokenToToken(tokens.cWETHv3)).to.equal(constants.ZERO_ADDRESS);
expect(await compoundV3Wrapper.tokenTocToken(tokens.WETH)).to.equal(constants.ZERO_ADDRESS);
});

it('WETH -> cWETHv3', async function () {
const { wallet, compoundV3Wrapper, cWETHv3 } = await loadFixture(initContracts);
const response = await compoundV3Wrapper.wrap(tokens.WETH);
const [received] = await trackReceivedTokenAndTx(ethers.provider, cWETHv3, wallet.address, async () => cWETHv3.supply(tokens.WETH, ether('1')));
expect(response.wrappedToken).to.equal(tokens.cWETHv3);
assertRoughlyEqualValues(response.rate, received, 1e-17);
});

it('cWETHv3 -> WETH', async function () {
const { wallet, compoundV3Wrapper, weth, cWETHv3 } = await loadFixture(initContracts);
await cWETHv3.supply(tokens.WETH, ether('2'));
const response = await compoundV3Wrapper.wrap(tokens.cWETHv3);
const [received] = await trackReceivedTokenAndTx(ethers.provider, weth, wallet.address, async () => cWETHv3.withdraw(tokens.WETH, ether('1')));
expect(response.wrappedToken).to.equal(tokens.WETH);
assertRoughlyEqualValues(response.rate, received, 1e-17);
});
});

0 comments on commit 41a6ee3

Please sign in to comment.