mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'fix/111' into 'master'
Resolve "Add a COMP-governance aware aToken" Closes #111 See merge request aave-tech/protocol-v2!121
This commit is contained in:
commit
19b3d987f8
|
@ -4,6 +4,7 @@ pragma solidity ^0.6.8;
|
|||
import {ReserveConfiguration} from '../libraries/configuration/ReserveConfiguration.sol';
|
||||
import {UserConfiguration} from '../libraries/configuration/UserConfiguration.sol';
|
||||
import {ReserveLogic} from '../libraries/logic/ReserveLogic.sol';
|
||||
import {ILendingPoolAddressesProvider} from './ILendingPoolAddressesProvider.sol';
|
||||
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
@ -368,6 +369,8 @@ interface ILendingPool {
|
|||
|
||||
function getReservesList() external view returns (address[] memory);
|
||||
|
||||
function getAddressesProvider() external view returns (ILendingPoolAddressesProvider);
|
||||
|
||||
/**
|
||||
* @dev Set the _pause state
|
||||
* @param val the boolean value to set the current pause state of LendingPool
|
||||
|
|
|
@ -753,7 +753,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
/**
|
||||
* @dev returns the addresses provider
|
||||
**/
|
||||
function getAddressesProvider() external view returns (ILendingPoolAddressesProvider) {
|
||||
function getAddressesProvider() external override view returns (ILendingPoolAddressesProvider) {
|
||||
return _addressesProvider;
|
||||
}
|
||||
|
||||
|
|
|
@ -188,10 +188,10 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
ILendingPool internal pool;
|
||||
|
||||
/**
|
||||
* @dev only the lending pool manager can call functions affected by this modifier
|
||||
* @dev only the aave admin can call functions affected by this modifier
|
||||
**/
|
||||
modifier onlyAaveAdmin {
|
||||
require(addressesProvider.getAaveAdmin() == msg.sender, Errors.LPC_CALLER_NOT_AAVE_ADMIN);
|
||||
require(addressesProvider.getAaveAdmin() == msg.sender, Errors.CALLER_NOT_AAVE_ADMIN);
|
||||
_;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@ pragma solidity ^0.6.8;
|
|||
* - P = Pausable
|
||||
*/
|
||||
library Errors {
|
||||
//common errors
|
||||
string public constant CALLER_NOT_AAVE_ADMIN = '33'; // 'The caller must be the aave admin'
|
||||
|
||||
//contract specific errors
|
||||
string public constant VL_AMOUNT_NOT_GREATER_THAN_0 = '1'; // 'Amount must be greater than 0'
|
||||
string public constant VL_NO_ACTIVE_RESERVE = '2'; // 'Action requires an active reserve'
|
||||
string public constant VL_RESERVE_FROZEN = '3'; // 'Action cannot be performed because the reserve is frozen'
|
||||
|
@ -49,7 +53,6 @@ library Errors {
|
|||
string public constant AT_CANNOT_GIVE_ALLVWANCE_TO_HIMSELF = '30'; // 'User cannot give allowance to himself'
|
||||
string public constant AT_TRANSFER_AMOUNT_NOT_GT_0 = '31'; // 'Transferred amount needs to be greater than zero'
|
||||
string public constant RL_RESERVE_ALREADY_INITIALIZED = '32'; // 'Reserve has already been initialized'
|
||||
string public constant LPC_CALLER_NOT_AAVE_ADMIN = '33'; // 'The caller must be the aave admin'
|
||||
string public constant LPC_RESERVE_LIQUIDITY_NOT_0 = '34'; // 'The liquidity of the reserve needs to be 0'
|
||||
string public constant LPC_INVALID_ATOKEN_POOL_ADDRESS = '35'; // 'The liquidity of the reserve needs to be 0'
|
||||
string public constant LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36'; // 'The liquidity of the reserve needs to be 0'
|
||||
|
|
34
contracts/mocks/tokens/MintableDelegationERC20.sol
Normal file
34
contracts/mocks/tokens/MintableDelegationERC20.sol
Normal file
|
@ -0,0 +1,34 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {ERC20} from '../../dependencies/openzeppelin/contracts/ERC20.sol';
|
||||
|
||||
/**
|
||||
* @title ERC20Mintable
|
||||
* @dev ERC20 minting logic
|
||||
*/
|
||||
contract MintableDelegationERC20 is ERC20 {
|
||||
address public delegatee;
|
||||
|
||||
constructor(
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
uint8 decimals
|
||||
) public ERC20(name, symbol) {
|
||||
_setupDecimals(decimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to mint tokensp
|
||||
* @param value The amount of tokens to mint.
|
||||
* @return A boolean that indicates if the operation was successful.
|
||||
*/
|
||||
function mint(uint256 value) public returns (bool) {
|
||||
_mint(msg.sender, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function delegate(address delegateeAddress) external {
|
||||
delegatee = delegateeAddress;
|
||||
}
|
||||
}
|
72
contracts/tokenization/DelegationAwareAToken.sol
Normal file
72
contracts/tokenization/DelegationAwareAToken.sol
Normal file
|
@ -0,0 +1,72 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {AToken} from './AToken.sol';
|
||||
import {ILendingPool} from '../interfaces/ILendingPool.sol';
|
||||
import {Errors} from '../libraries/helpers/Errors.sol';
|
||||
|
||||
/**
|
||||
* @title IDelegationToken
|
||||
* @dev implements an interface for tokens that have a delegation function
|
||||
**/
|
||||
interface IDelegationToken {
|
||||
function delegate(address delegatee) external;
|
||||
}
|
||||
|
||||
/**
|
||||
* @title Aave AToken with delegation capabilities
|
||||
*
|
||||
* @dev Implementation of the interest bearing token for the Aave protocol. This version of the aToken
|
||||
* adds a function which gives the Aave protocol the ability to delegate voting power of the underlying asset.
|
||||
* The underlying asset needs to be compatible with the COMP delegation interface
|
||||
* @author Aave
|
||||
*/
|
||||
contract DelegationAwareAToken is AToken {
|
||||
/**
|
||||
* @dev only the aave admin can call this function
|
||||
**/
|
||||
modifier onlyAaveAdmin {
|
||||
require(
|
||||
_msgSender() == ILendingPool(POOL).getAddressesProvider().getAaveAdmin(),
|
||||
Errors.CALLER_NOT_AAVE_ADMIN
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
constructor(
|
||||
ILendingPool pool,
|
||||
address underlyingAssetAddress,
|
||||
address reserveTreasury,
|
||||
string memory tokenName,
|
||||
string memory tokenSymbol,
|
||||
address incentivesController
|
||||
)
|
||||
public
|
||||
AToken(
|
||||
pool,
|
||||
underlyingAssetAddress,
|
||||
reserveTreasury,
|
||||
tokenName,
|
||||
tokenSymbol,
|
||||
incentivesController
|
||||
)
|
||||
{}
|
||||
|
||||
function initialize(
|
||||
uint8 _underlyingAssetDecimals,
|
||||
string calldata _tokenName,
|
||||
string calldata _tokenSymbol
|
||||
) external virtual override initializer {
|
||||
_setName(_tokenName);
|
||||
_setSymbol(_tokenSymbol);
|
||||
_setDecimals(_underlyingAssetDecimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev delegates voting power of the underlying asset to a specific address
|
||||
* @param delegatee the address that will receive the delegation
|
||||
**/
|
||||
function delegateUnderlyingTo(address delegatee) external onlyAaveAdmin {
|
||||
IDelegationToken(UNDERLYING_ASSET_ADDRESS).delegate(delegatee);
|
||||
}
|
||||
}
|
|
@ -163,26 +163,25 @@
|
|||
},
|
||||
"ReserveLogic": {
|
||||
"buidlerevm": {
|
||||
"address": "0x78Ee8Fb9fE5abD5e347Fc94c2fb85596d1f60e3c",
|
||||
"address": "0xFAe0fd738dAbc8a0426F47437322b6d026A9FD95",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"GenericLogic": {
|
||||
"buidlerevm": {
|
||||
"address": "0x920d847fE49E54C19047ba8bc236C45A8068Bca7",
|
||||
"address": "0x6082731fdAba4761277Fb31299ebC782AD3bCf24",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"ValidationLogic": {
|
||||
"buidlerevm": {
|
||||
"address": "0xA4765Ff72A9F3CfE73089bb2c3a41B838DF71574",
|
||||
"address": "0x8456161947DFc1fC159A0B26c025cD2b4bba0c3e",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"LendingPool": {
|
||||
"buidlerevm": {
|
||||
"address": "0x35c1419Da7cf0Ff885B8Ef8EA9242FEF6800c99b",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
"address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e"
|
||||
}
|
||||
},
|
||||
"LendingPoolConfigurator": {
|
||||
|
@ -198,7 +197,7 @@
|
|||
},
|
||||
"ATokensAndRatesHelper": {
|
||||
"buidlerevm": {
|
||||
"address": "0x920d847fE49E54C19047ba8bc236C45A8068Bca7",
|
||||
"address": "0x06bA8d8af0dF898D0712DffFb0f862cC51AF45c2",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
|
@ -265,5 +264,17 @@
|
|||
"address": "0x920d847fE49E54C19047ba8bc236C45A8068Bca7",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"MintableDelegationERC20": {
|
||||
"buidlerevm": {
|
||||
"address": "0x77B0b5636fEA30eA79BB65AeCCdb599997A849A8",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"AToken": {
|
||||
"buidlerevm": {
|
||||
"address": "0x78Ee8Fb9fE5abD5e347Fc94c2fb85596d1f60e3c",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
ATokensAndRatesHelperFactory,
|
||||
ChainlinkProxyPriceProviderFactory,
|
||||
DefaultReserveInterestRateStrategyFactory,
|
||||
DelegationAwareATokenFactory,
|
||||
InitializableAdminUpgradeabilityProxyFactory,
|
||||
LendingPoolAddressesProviderFactory,
|
||||
LendingPoolAddressesProviderRegistryFactory,
|
||||
|
@ -31,6 +32,7 @@ import {
|
|||
LendingPoolFactory,
|
||||
LendingPoolLibraryAddresses,
|
||||
LendingRateOracleFactory,
|
||||
MintableDelegationErc20Factory,
|
||||
MintableErc20Factory,
|
||||
MockAggregatorFactory,
|
||||
MockATokenFactory,
|
||||
|
@ -47,6 +49,7 @@ import {withSaveAndVerify, registerContractInJsonDb, linkBytecode} from './contr
|
|||
import {StableAndVariableTokensHelperFactory} from '../types/StableAndVariableTokensHelperFactory';
|
||||
import {MockStableDebtToken} from '../types/MockStableDebtToken';
|
||||
import {MockVariableDebtToken} from '../types/MockVariableDebtToken';
|
||||
import {MintableDelegationErc20} from '../types/MintableDelegationErc20';
|
||||
|
||||
export const deployLendingPoolAddressesProvider = async (verify?: boolean) =>
|
||||
withSaveAndVerify(
|
||||
|
@ -254,6 +257,16 @@ export const deployMintableERC20 = async (
|
|||
verify
|
||||
);
|
||||
|
||||
export const deployMintableDelegationERC20 = async (
|
||||
args: [string, string, string],
|
||||
verify?: boolean
|
||||
): Promise<MintableDelegationErc20> =>
|
||||
withSaveAndVerify(
|
||||
await new MintableDelegationErc20Factory(await getFirstSigner()).deploy(...args),
|
||||
eContractid.MintableDelegationERC20,
|
||||
args,
|
||||
verify
|
||||
);
|
||||
export const deployDefaultReserveInterestRateStrategy = async (
|
||||
args: [tEthereumAddress, string, string, string, string, string],
|
||||
verify: boolean
|
||||
|
@ -313,6 +326,32 @@ export const deployGenericAToken = async (
|
|||
);
|
||||
};
|
||||
|
||||
export const deployDelegationAwareAToken = async (
|
||||
[poolAddress, underlyingAssetAddress, name, symbol, incentivesController]: [
|
||||
tEthereumAddress,
|
||||
tEthereumAddress,
|
||||
string,
|
||||
string,
|
||||
tEthereumAddress
|
||||
],
|
||||
verify: boolean
|
||||
) => {
|
||||
const args: [
|
||||
tEthereumAddress,
|
||||
tEthereumAddress,
|
||||
tEthereumAddress,
|
||||
string,
|
||||
string,
|
||||
tEthereumAddress
|
||||
] = [poolAddress, underlyingAssetAddress, ZERO_ADDRESS, name, symbol, incentivesController];
|
||||
return withSaveAndVerify(
|
||||
await new DelegationAwareATokenFactory(await getFirstSigner()).deploy(...args),
|
||||
eContractid.AToken,
|
||||
args,
|
||||
verify
|
||||
);
|
||||
};
|
||||
|
||||
export const deployAllMockTokens = async (verify?: boolean) => {
|
||||
const tokens: {[symbol: string]: MockContract | MintableERC20} = {};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ export enum eContractid {
|
|||
Example = 'Example',
|
||||
LendingPoolAddressesProvider = 'LendingPoolAddressesProvider',
|
||||
MintableERC20 = 'MintableERC20',
|
||||
MintableDelegationERC20 = 'MintableDelegationERC20',
|
||||
LendingPoolAddressesProviderRegistry = 'LendingPoolAddressesProviderRegistry',
|
||||
LendingPoolParametersProvider = 'LendingPoolParametersProvider',
|
||||
LendingPoolConfigurator = 'LendingPoolConfigurator',
|
||||
|
@ -47,6 +48,7 @@ export enum eContractid {
|
|||
WalletBalanceProvider = 'WalletBalanceProvider',
|
||||
AToken = 'AToken',
|
||||
MockAToken = 'MockAToken',
|
||||
DelegationAwareAToken = 'DelegationAwareAToken',
|
||||
MockStableDebtToken = 'MockStableDebtToken',
|
||||
MockVariableDebtToken = 'MockVariableDebtToken',
|
||||
AaveProtocolTestHelpers = 'AaveProtocolTestHelpers',
|
||||
|
@ -72,6 +74,10 @@ export enum eContractid {
|
|||
* - P = Pausable
|
||||
*/
|
||||
export enum ProtocolErrors {
|
||||
//common errors
|
||||
CALLER_NOT_AAVE_ADMIN = '33', // 'The caller must be the aave admin'
|
||||
|
||||
//contract specific errors
|
||||
VL_AMOUNT_NOT_GREATER_THAN_0 = '1', // 'Amount must be greater than 0'
|
||||
VL_NO_ACTIVE_RESERVE = '2', // 'Action requires an active reserve'
|
||||
VL_RESERVE_FROZEN = '3', // 'Action requires an unfrozen reserve'
|
||||
|
@ -104,7 +110,6 @@ export enum ProtocolErrors {
|
|||
AT_CANNOT_GIVE_ALLVWANCE_TO_HIMSELF = '30', // 'User cannot give allowance to himself'
|
||||
AT_TRANSFER_AMOUNT_NOT_GT_0 = '31', // 'Transferred amount needs to be greater than zero'
|
||||
RL_RESERVE_ALREADY_INITIALIZED = '32', // 'Reserve has already been initialized'
|
||||
LPC_CALLER_NOT_AAVE_ADMIN = '33', // 'The caller must be the aave admin'
|
||||
LPC_RESERVE_LIQUIDITY_NOT_0 = '34', // 'The liquidity of the reserve needs to be 0'
|
||||
LPC_INVALID_ATOKEN_POOL_ADDRESS = '35', // 'The liquidity of the reserve needs to be 0'
|
||||
LPC_INVALID_STABLE_DEBT_TOKEN_POOL_ADDRESS = '36', // 'The liquidity of the reserve needs to be 0'
|
||||
|
|
58
test/delegation-aware-atoken.spec.ts
Normal file
58
test/delegation-aware-atoken.spec.ts
Normal file
|
@ -0,0 +1,58 @@
|
|||
import {MAX_UINT_AMOUNT, ZERO_ADDRESS} from '../helpers/constants';
|
||||
import {BUIDLEREVM_CHAINID} from '../helpers/buidler-constants';
|
||||
import {buildPermitParams, getSignatureFromTypedData} from '../helpers/contracts-helpers';
|
||||
import {expect} from 'chai';
|
||||
import {ethers} from 'ethers';
|
||||
import {eEthereumNetwork, ProtocolErrors} from '../helpers/types';
|
||||
import {makeSuite, TestEnv} from './helpers/make-suite';
|
||||
import {BRE} from '../helpers/misc-utils';
|
||||
import {
|
||||
ConfigNames,
|
||||
getATokenDomainSeparatorPerNetwork,
|
||||
loadPoolConfig,
|
||||
} from '../helpers/configuration';
|
||||
import {waitForTx} from '../helpers/misc-utils';
|
||||
import {
|
||||
deployDelegationAwareAToken,
|
||||
deployMintableDelegationERC20,
|
||||
} from '../helpers/contracts-deployments';
|
||||
import {DelegationAwareATokenFactory} from '../types';
|
||||
import {DelegationAwareAToken} from '../types/DelegationAwareAToken';
|
||||
import {MintableDelegationErc20} from '../types/MintableDelegationErc20';
|
||||
|
||||
const {parseEther} = ethers.utils;
|
||||
|
||||
makeSuite('AToken: underlying delegation', (testEnv: TestEnv) => {
|
||||
const poolConfig = loadPoolConfig(ConfigNames.Commons);
|
||||
let delegationAToken = <DelegationAwareAToken>{};
|
||||
let delegationERC20 = <MintableDelegationErc20>{};
|
||||
|
||||
it('Deploys a new MintableDelegationERC20 and a DelegationAwareAToken', async () => {
|
||||
const {pool} = testEnv;
|
||||
|
||||
delegationERC20 = await deployMintableDelegationERC20(['DEL', 'DEL', '18']);
|
||||
|
||||
delegationAToken = await deployDelegationAwareAToken(
|
||||
[pool.address, delegationERC20.address, 'aDEL', 'aDEL', ZERO_ADDRESS],
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('Tries to delegate with the caller not being the Aave admin', async () => {
|
||||
const {users} = testEnv;
|
||||
|
||||
await expect(
|
||||
delegationAToken.connect(users[1].signer).delegateUnderlyingTo(users[2].address)
|
||||
).to.be.revertedWith(ProtocolErrors.CALLER_NOT_AAVE_ADMIN);
|
||||
});
|
||||
|
||||
it('Tries to delegate to user 2', async () => {
|
||||
const {users} = testEnv;
|
||||
|
||||
await delegationAToken.delegateUnderlyingTo(users[2].address);
|
||||
|
||||
const delegateeAddress = await delegationERC20.delegatee();
|
||||
|
||||
expect(delegateeAddress).to.be.equal(users[2].address);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user