Skip to content

Commit

Permalink
Merge pull request #104 from 1inch/feature/aave-wrapper-v3
Browse files Browse the repository at this point in the history
[SC-1028] AaveWrapperV3
  • Loading branch information
zZoMROT authored Dec 21, 2023
2 parents 318ee4a + bcf45df commit 2e6cd71
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 0 deletions.
15 changes: 15 additions & 0 deletions contracts/interfaces/ILendingPoolV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

// AaveProtocolDataProvider
interface ILendingPoolV3 {
function getReserveTokensAddresses(address asset) external view returns (address aTokenAddress, address stableDebtTokenAddress, address variableDebtTokenAddress);

struct TokenData {
string symbol;
address tokenAddress;
}

function getAllReservesTokens() external view returns (TokenData[] memory);
}
54 changes: 54 additions & 0 deletions contracts/wrappers/AaveWrapperV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

import "../interfaces/ILendingPoolV3.sol";
import "../interfaces/IWrapper.sol";

contract AaveWrapperV3 is IWrapper {
// solhint-disable-next-line var-name-mixedcase
ILendingPoolV3 private immutable _LENDING_POOL;

mapping(IERC20 => IERC20) public aTokenToToken;
mapping(IERC20 => IERC20) public tokenToaToken;

constructor(ILendingPoolV3 lendingPool) {
_LENDING_POOL = lendingPool;
}

function addMarkets(IERC20[] memory tokens) external {
unchecked {
for (uint256 i = 0; i < tokens.length; i++) {
(address aTokenAddress,,) = _LENDING_POOL.getReserveTokensAddresses(address(tokens[i]));
IERC20 aToken = IERC20(aTokenAddress);
if(aToken == IERC20(address(0))) revert NotAddedMarket();
aTokenToToken[aToken] = tokens[i];
tokenToaToken[tokens[i]] = aToken;
}
}
}

function removeMarkets(IERC20[] memory tokens) external {
unchecked {
for (uint256 i = 0; i < tokens.length; i++) {
(address aTokenAddress,,) = _LENDING_POOL.getReserveTokensAddresses(address(tokens[i]));
IERC20 aToken = IERC20(aTokenAddress);
if(aToken == IERC20(address(0))) revert NotRemovedMarket();
delete aTokenToToken[aToken];
delete tokenToaToken[tokens[i]];
}
}
}

function wrap(IERC20 token) external view override returns (IERC20 wrappedToken, uint256 rate) {
IERC20 underlying = aTokenToToken[token];
IERC20 aToken = tokenToaToken[token];
if (underlying != IERC20(address(0))) {
return (underlying, 1e18);
} else if (aToken != IERC20(address(0))) {
return (aToken, 1e18);
} else {
revert NotSupportedToken();
}
}
}
7 changes: 7 additions & 0 deletions deploy/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ async function addAaveTokens (aaveWrapperV2, AAWE_WRAPPER_TOKENS) {
}
}

async function getAllAave3ReservesTokens (lendingPoolV3Address) {
const lendingPoolV3 = await ethers.getContractAt('ILendingPoolV3', lendingPoolV3Address);
const tokens = await lendingPoolV3.getAllReservesTokens();
return tokens.map(token => token[1]);
}

const deployCompoundTokenWrapper = async (contractInfo, tokenName, deployments, deployer, deploymentName = `CompoundLikeWrapper_${contractInfo.name}`) => {
const comptroller = await ethers.getContractAt('IComptroller', contractInfo.address);
const cToken = (await comptroller.getAllMarkets()).filter(token => token !== tokenName);
Expand All @@ -45,6 +51,7 @@ const getContract = async (deployments, contractName, deploymentName = contractN

module.exports = {
addAaveTokens,
getAllAave3ReservesTokens,
deployCompoundTokenWrapper,
getContract,
};
5 changes: 5 additions & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ const tokens = {
XRA: '0x7025bab2ec90410de37f488d1298204cd4d6b29d',
aDAIV1: '0xfC1E690f61EFd961294b3e1Ce3313fBD8aa4f85d',
aDAIV2: '0x028171bCA77440897B824Ca71D1c56caC55b68A3',
aDAIV3: '0x018008bfb33d285247A21d44E50697654f754e63',
aETHV1: '0x3a3A65aAb0dd2A17E3F1947bA16138cd37d08c04',
aWETHV2: '0x030bA81f1c18d280636F32af80b9AAd02Cf0854e',
aWETHV3: '0x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8',
cDAI: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643',
cETH: '0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5',
iETH: '0xB983E01458529665007fF7E0CDdeCDB74B967Eb6',
Expand Down Expand Up @@ -74,6 +76,9 @@ const deployParams = {
AaveWrapperV2: {
lendingPool: '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9',
},
AaveWrapperV3: {
lendingPool: '0x7B4EB56E7CD4b454BA8ff71E4518426369a138a3',
},
UniswapV3: {
factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
initcodeHash: '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54',
Expand Down
93 changes: 93 additions & 0 deletions test/wrappers/AaveWrapperV3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
const { ethers } = require('hardhat');
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
const { expect, ether, deployContract } = require('@1inch/solidity-utils');
const { tokens, deployParams: { AaveWrapperV3 } } = require('../helpers.js');
const { getAllAave3ReservesTokens } = require('../../deploy/utils.js');

describe('AaveWrapperV3', function () {
async function initContracts () {
const aaveWrapper = await deployContract('AaveWrapperV3', [AaveWrapperV3.lendingPool]);
await aaveWrapper.addMarkets([tokens.DAI, tokens.WETH]);
return { aaveWrapper };
}

it('should retrun all reserves tokens', async function () {
const tokens = await getAllAave3ReservesTokens(AaveWrapperV3.lendingPool);
tokens.forEach(token => {
expect(ethers.isAddress(token)).to.equal(true);
});
});

it('should revert with non-supported token', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
const tokens = await getAllAave3ReservesTokens(AaveWrapperV3.lendingPool);
const token = tokens[tokens.length - 1];
await expect(aaveWrapper.wrap(token)).to.be.revertedWithCustomError(aaveWrapper, 'NotSupportedToken');
});

it('should correct add market', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
const aaveV3Reserves = await getAllAave3ReservesTokens(AaveWrapperV3.lendingPool);
const token = aaveV3Reserves[aaveV3Reserves.length - 1];
await aaveWrapper.addMarkets([token]);
const response = await aaveWrapper.wrap(token);
expect(response.rate).to.equal(ether('1'));
});

it('should correct add already added market', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
await aaveWrapper.addMarkets([tokens.DAI]);
const response = await aaveWrapper.wrap(tokens.DAI);
expect(response.rate).to.equal(ether('1'));
expect(response.wrappedToken).to.equal(tokens.aDAIV3);
});

it('should revert if added market is incorrect', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
await expect(aaveWrapper.addMarkets([tokens.aDAIV1])).to.be.revertedWithCustomError(aaveWrapper, 'NotAddedMarket');
});

it('should revert if one of added markets is incorrect', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
await expect(aaveWrapper.addMarkets([tokens.DAI, tokens.aDAIV1])).to.be.revertedWithCustomError(aaveWrapper, 'NotAddedMarket');
});

it('should correct remove market', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
await aaveWrapper.removeMarkets([tokens.DAI]);
await expect(aaveWrapper.wrap(tokens.DAI)).to.be.revertedWithCustomError(aaveWrapper, 'NotSupportedToken');
});

it('should revert if removed market is incorrect', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
await expect(aaveWrapper.removeMarkets([tokens.aDAIV1])).to.be.revertedWithCustomError(aaveWrapper, 'NotRemovedMarket');
});

it('dai -> adai', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
const response = await aaveWrapper.wrap(tokens.DAI);
expect(response.rate).to.equal(ether('1'));
expect(response.wrappedToken).to.equal(tokens.aDAIV3);
});

it('adai -> dai', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
const response = await aaveWrapper.wrap(tokens.aDAIV3);
expect(response.rate).to.equal(ether('1'));
expect(response.wrappedToken).to.equal(tokens.DAI);
});

it('weth -> aweth', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
const response = await aaveWrapper.wrap(tokens.WETH);
expect(response.rate).to.equal(ether('1'));
expect(response.wrappedToken).to.equal(tokens.aWETHV3);
});

it('aweth -> weth', async function () {
const { aaveWrapper } = await loadFixture(initContracts);
const response = await aaveWrapper.wrap(tokens.aWETHV3);
expect(response.rate).to.equal(ether('1'));
expect(response.wrappedToken).to.equal(tokens.WETH);
});
});

0 comments on commit 2e6cd71

Please sign in to comment.