-
Notifications
You must be signed in to change notification settings - Fork 80
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 #104 from 1inch/feature/aave-wrapper-v3
[SC-1028] AaveWrapperV3
- Loading branch information
Showing
5 changed files
with
174 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,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); | ||
} |
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,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(); | ||
} | ||
} | ||
} |
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
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
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,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); | ||
}); | ||
}); |