aave-protocol-v2/helpers/contracts-helpers.ts

602 lines
18 KiB
TypeScript
Raw Normal View History

2020-07-13 08:54:08 +00:00
import {Contract, Signer, utils, ethers} from 'ethers';
2020-08-25 12:15:35 +00:00
import {CommonsConfig} from '../config/commons';
2020-07-13 08:54:08 +00:00
import {getDb, BRE} from './misc-utils';
import {
tEthereumAddress,
eContractid,
tStringTokenSmallUnits,
eEthereumNetwork,
AavePools,
iParamsPerNetwork,
iParamsPerPool,
2020-08-20 15:38:57 +00:00
TokenContractId,
iMultiPoolsAssets,
IReserveParams,
PoolConfiguration,
2020-07-13 08:54:08 +00:00
} from './types';
2020-08-25 12:15:35 +00:00
import {MintableErc20 as MintableERC20} from '../types/MintableErc20';
2020-07-13 08:54:08 +00:00
import {readArtifact} from '@nomiclabs/buidler/plugins';
import {Artifact} from '@nomiclabs/buidler/types';
import {DefaultReserveInterestRateStrategy} from '../types/DefaultReserveInterestRateStrategy';
import {MockFlashLoanReceiver} from '../types/MockFlashLoanReceiver';
import {WalletBalanceProvider} from '../types/WalletBalanceProvider';
import {AToken} from '../types/AToken';
import {AaveProtocolTestHelpers} from '../types/AaveProtocolTestHelpers';
import BigNumber from 'bignumber.js';
import {StableDebtToken} from '../types/StableDebtToken';
import {VariableDebtToken} from '../types/VariableDebtToken';
2020-08-20 15:38:57 +00:00
import {MockContract} from 'ethereum-waffle';
2020-08-25 12:15:35 +00:00
import {getReservesConfigByPool} from './configuration';
2020-08-20 15:38:57 +00:00
import {verifyContract} from './etherscan-verification';
2020-10-15 17:19:02 +00:00
import {getFirstSigner, getGenericLogic} from './contracts-getters';
2020-08-25 12:15:35 +00:00
const {
ProtocolGlobalParams: {UsdAddress},
} = CommonsConfig;
export type MockTokenMap = {[symbol: string]: MintableERC20};
2020-10-08 13:41:48 +00:00
import {ZERO_ADDRESS} from './constants';
import {MockSwapAdapter} from '../types/MockSwapAdapter';
2020-09-15 14:02:21 +00:00
import {signTypedData_v4, TypedData} from 'eth-sig-util';
import {fromRpcSig, ECDSASignature} from 'ethereumjs-util';
2020-10-15 17:19:02 +00:00
import {getIErc20Detailed} from './contracts-getters';
import {
ChainlinkProxyPriceProviderFactory,
2020-10-15 17:19:02 +00:00
GenericLogicFactory,
InitializableAdminUpgradeabilityProxyFactory,
LendingPoolAddressesProviderFactory,
LendingPoolAddressesProviderRegistryFactory,
LendingPoolCollateralManagerFactory,
2020-10-15 17:19:02 +00:00
LendingPoolConfiguratorFactory,
LendingPoolFactory,
LendingPoolLibraryAddresses,
LendingRateOracleFactory,
MockAggregatorFactory,
2020-10-15 17:19:02 +00:00
MockFlashLoanReceiverFactory,
MockSwapAdapterFactory,
PriceOracleFactory,
2020-10-15 17:19:02 +00:00
ReserveLogicFactory,
WalletBalanceProviderFactory,
} from '../types';
2020-07-13 08:54:08 +00:00
export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
2020-05-29 14:55:31 +00:00
const currentNetwork = BRE.network.name;
if (currentNetwork !== 'buidlerevm' && !currentNetwork.includes('coverage')) {
2020-05-29 14:55:31 +00:00
console.log(`*** ${contractId} ***\n`);
console.log(`Network: ${currentNetwork}`);
console.log(`tx: ${contractInstance.deployTransaction.hash}`);
console.log(`contract address: ${contractInstance.address}`);
console.log(`deployer address: ${contractInstance.deployTransaction.from}`);
console.log(`gas price: ${contractInstance.deployTransaction.gasPrice}`);
console.log(`gas used: ${contractInstance.deployTransaction.gasLimit}`);
console.log(`\n******`);
console.log();
}
await getDb()
.set(`${contractId}.${currentNetwork}`, {
address: contractInstance.address,
deployer: contractInstance.deployTransaction.from,
})
.write();
};
2020-07-13 08:54:08 +00:00
export const insertContractAddressInDb = async (id: eContractid, address: tEthereumAddress) =>
await getDb()
.set(`${id}.${BRE.network.name}`, {
address,
})
.write();
2020-05-29 14:55:31 +00:00
export const getEthersSigners = async (): Promise<Signer[]> =>
2020-08-19 12:23:41 +00:00
await Promise.all(await BRE.ethers.getSigners());
2020-05-29 14:55:31 +00:00
2020-07-13 08:54:08 +00:00
export const getEthersSignersAddresses = async (): Promise<tEthereumAddress[]> =>
2020-08-19 12:23:41 +00:00
await Promise.all((await BRE.ethers.getSigners()).map((signer) => signer.getAddress()));
2020-05-29 14:55:31 +00:00
export const getCurrentBlock = async () => {
return BRE.ethers.provider.getBlockNumber();
};
export const decodeAbiNumber = (data: string): number =>
2020-07-13 08:54:08 +00:00
parseInt(utils.defaultAbiCoder.decode(['uint256'], data).toString());
2020-05-29 14:55:31 +00:00
2020-08-10 18:20:08 +00:00
export const deployContract = async <ContractType extends Contract>(
2020-05-29 14:55:31 +00:00
contractName: string,
args: any[]
): Promise<ContractType> => {
2020-08-19 12:23:41 +00:00
const contract = (await (await BRE.ethers.getContractFactory(contractName)).deploy(
2020-05-29 14:55:31 +00:00
...args
)) as ContractType;
await registerContractInJsonDb(<eContractid>contractName, contract);
return contract;
};
2020-10-15 17:19:02 +00:00
export const withSaveAndVerify = async (
instance: Contract,
id: string,
args: (string | string[])[],
verify?: boolean
) => {
await registerContractInJsonDb(id, instance);
if (verify) {
await verifyContract(id, instance.address, args);
}
return instance;
};
export const getContract = async <ContractType extends Contract>(
2020-05-29 14:55:31 +00:00
contractName: string,
address: string
2020-07-13 08:54:08 +00:00
): Promise<ContractType> => (await BRE.ethers.getContractAt(contractName, address)) as ContractType;
2020-05-29 14:55:31 +00:00
2020-10-15 17:19:02 +00:00
export const deployLendingPoolAddressesProvider = async (verify?: boolean) =>
withSaveAndVerify(
await new LendingPoolAddressesProviderFactory(await getFirstSigner()).deploy(),
2020-08-20 15:38:57 +00:00
eContractid.LendingPoolAddressesProvider,
2020-10-15 17:19:02 +00:00
[],
verify
2020-08-20 15:38:57 +00:00
);
2020-05-29 14:55:31 +00:00
2020-10-15 17:19:02 +00:00
export const deployLendingPoolAddressesProviderRegistry = async (verify?: boolean) =>
withSaveAndVerify(
await new LendingPoolAddressesProviderRegistryFactory(await getFirstSigner()).deploy(),
eContractid.LendingPoolAddressesProviderRegistry,
2020-10-15 17:19:02 +00:00
[],
verify
);
2020-10-15 17:19:02 +00:00
export const deployLendingPoolConfigurator = async (verify?: boolean) =>
withSaveAndVerify(
await new LendingPoolConfiguratorFactory(await getFirstSigner()).deploy(),
2020-08-20 15:38:57 +00:00
eContractid.LendingPoolConfigurator,
2020-10-15 17:19:02 +00:00
[],
verify
2020-08-20 15:38:57 +00:00
);
2020-06-20 23:40:03 +00:00
2020-10-15 17:19:02 +00:00
export const deployReserveLogicLibrary = async (verify?: boolean) =>
withSaveAndVerify(
await new ReserveLogicFactory(await getFirstSigner()).deploy(),
eContractid.ReserveLogic,
[],
verify
);
2020-06-20 23:40:03 +00:00
2020-10-15 17:19:02 +00:00
export const deployGenericLogic = async (verify?: boolean) =>
withSaveAndVerify(
await new GenericLogicFactory(await getFirstSigner()).deploy(),
eContractid.GenericLogic,
[],
verify
2020-06-20 23:40:03 +00:00
);
2020-07-03 09:15:30 +00:00
2020-10-15 17:19:02 +00:00
export const deployValidationLogic = async (
reserveLogic: Contract,
genericLogic: Contract,
verify?: boolean
) => {
2020-06-20 23:40:03 +00:00
const validationLogicArtifact = await readArtifact(
BRE.config.paths.artifacts,
eContractid.ValidationLogic
);
2020-06-25 22:39:28 +00:00
const linkedValidationLogicByteCode = linkBytecode(validationLogicArtifact, {
2020-06-20 23:40:03 +00:00
[eContractid.ReserveLogic]: reserveLogic.address,
[eContractid.GenericLogic]: genericLogic.address,
});
2020-07-03 09:15:30 +00:00
2020-06-20 23:40:03 +00:00
const validationLogicFactory = await BRE.ethers.getContractFactory(
validationLogicArtifact.abi,
linkedValidationLogicByteCode
);
2020-07-03 09:15:30 +00:00
2020-06-20 23:40:03 +00:00
const validationLogic = await (await validationLogicFactory.deploy()).deployed();
2020-10-15 17:19:02 +00:00
return withSaveAndVerify(validationLogic, eContractid.ValidationLogic, [], verify);
};
2020-06-26 12:34:44 +00:00
2020-10-15 17:19:02 +00:00
export const deployAaveLibraries = async (
verify?: boolean
): Promise<LendingPoolLibraryAddresses> => {
const reserveLogic = await deployReserveLogicLibrary(verify);
const genericLogic = await deployGenericLogic(verify);
const validationLogic = await deployValidationLogic(reserveLogic, genericLogic, verify);
// Hardcoded solidity placeholders, if any library changes path this will fail.
// Placeholder can be calculated via solidity keccak, but the LendingPoolLibraryAddresses Type seems to
// require a hardcoded string.
//
// how-to: PLACEHOLDER = solidityKeccak256(['string'], `${libPath}:${libName}`).slice(2, 36)
// '__$PLACEHOLDER$__'
// or grab placeholdes from LendingPoolLibraryAddresses at Typechain generation.
return {
['__$5201a97c05ba6aa659e2f36a933dd51801$__']: reserveLogic.address,
['__$d3b4366daeb9cadc7528af6145b50b2183$__']: genericLogic.address,
['__$4c26be947d349222af871a3168b3fe584b$__']: validationLogic.address,
};
2020-07-13 08:54:08 +00:00
};
2020-06-26 12:34:44 +00:00
2020-08-20 15:38:57 +00:00
export const deployLendingPool = async (verify?: boolean) => {
2020-10-15 17:19:02 +00:00
const libraries = await deployAaveLibraries(verify);
return withSaveAndVerify(
await new LendingPoolFactory(libraries, await getFirstSigner()).deploy(),
eContractid.LendingPool,
[],
verify
2020-06-26 12:34:44 +00:00
);
2020-07-13 08:54:08 +00:00
};
2020-10-15 17:19:02 +00:00
export const deployPriceOracle = async (verify?: boolean) =>
withSaveAndVerify(
await new PriceOracleFactory(await getFirstSigner()).deploy(),
eContractid.PriceOracle,
[],
verify
);
2020-10-15 17:19:02 +00:00
export const deployLendingRateOracle = async (verify?: boolean) =>
withSaveAndVerify(
await new LendingRateOracleFactory(await getFirstSigner()).deploy(),
eContractid.LendingRateOracle,
[],
verify
);
2020-10-15 17:19:02 +00:00
export const deployMockAggregator = async (price: tStringTokenSmallUnits, verify?: boolean) =>
withSaveAndVerify(
await new MockAggregatorFactory(await getFirstSigner()).deploy(price),
eContractid.MockAggregator,
[price],
verify
);
2020-08-21 11:07:32 +00:00
export const deployChainlinkProxyPriceProvider = async (
2020-10-15 17:19:02 +00:00
args: [tEthereumAddress[], tEthereumAddress[], tEthereumAddress],
2020-08-21 11:07:32 +00:00
verify?: boolean
2020-10-15 17:19:02 +00:00
) =>
withSaveAndVerify(
await new ChainlinkProxyPriceProviderFactory(await getFirstSigner()).deploy(...args),
eContractid.ChainlinkProxyPriceProvider,
args,
verify
2020-08-21 11:07:32 +00:00
);
2020-09-24 15:48:29 +00:00
export const deployLendingPoolCollateralManager = async (verify?: boolean) => {
2020-10-15 17:19:02 +00:00
const genericLogic = await getGenericLogic();
const libraries = {
// See deployAaveLibraries() function
['__$d3b4366daeb9cadc7528af6145b50b2183$__']: genericLogic.address,
};
return withSaveAndVerify(
await new LendingPoolCollateralManagerFactory(libraries, await getFirstSigner()).deploy(),
eContractid.LendingPoolCollateralManager,
[],
verify
);
2020-07-13 08:54:08 +00:00
};
2020-10-15 17:19:02 +00:00
export const deployInitializableAdminUpgradeabilityProxy = async (verify?: boolean) =>
withSaveAndVerify(
await new InitializableAdminUpgradeabilityProxyFactory(await getFirstSigner()).deploy(),
eContractid.InitializableAdminUpgradeabilityProxy,
2020-10-15 17:19:02 +00:00
[],
verify
);
2020-08-21 11:07:32 +00:00
export const deployMockFlashLoanReceiver = async (
addressesProvider: tEthereumAddress,
verify?: boolean
2020-10-15 17:19:02 +00:00
) =>
withSaveAndVerify(
await new MockFlashLoanReceiverFactory(await getFirstSigner()).deploy(addressesProvider),
2020-08-21 11:07:32 +00:00
eContractid.MockFlashLoanReceiver,
2020-10-15 17:19:02 +00:00
[addressesProvider],
verify
2020-08-21 11:07:32 +00:00
);
2020-08-21 11:07:32 +00:00
export const deployWalletBalancerProvider = async (
addressesProvider: tEthereumAddress,
verify?: boolean
2020-10-15 17:19:02 +00:00
) =>
withSaveAndVerify(
await new WalletBalanceProviderFactory(await getFirstSigner()).deploy(addressesProvider),
2020-08-21 11:07:32 +00:00
eContractid.WalletBalanceProvider,
2020-10-15 17:19:02 +00:00
[addressesProvider],
verify
2020-08-21 11:07:32 +00:00
);
2020-10-15 17:19:02 +00:00
export const deployMockSwapAdapter = async (addressesProvider: tEthereumAddress) =>
2020-10-15 17:19:02 +00:00
await new MockSwapAdapterFactory(await getFirstSigner()).deploy(addressesProvider);
2020-08-21 11:07:32 +00:00
export const deployAaveProtocolTestHelpers = async (
addressesProvider: tEthereumAddress,
verify?: boolean
) => {
const args = [addressesProvider];
const instance = await deployContract<AaveProtocolTestHelpers>(
eContractid.AaveProtocolTestHelpers,
args
);
2020-06-08 15:36:40 +00:00
2020-08-21 11:07:32 +00:00
if (verify) {
await verifyContract(eContractid.AaveProtocolTestHelpers, instance.address, args);
}
return instance;
};
export const deployMintableERC20 = async ([name, symbol, decimals]: [string, string, number]) =>
await deployContract<MintableERC20>(eContractid.MintableERC20, [name, symbol, decimals]);
2020-06-08 15:36:40 +00:00
export const deployDefaultReserveInterestRateStrategy = async (
[
2020-07-13 08:54:08 +00:00
addressesProvider,
baseVariableBorrowRate,
variableSlope1,
variableSlope2,
stableSlope1,
stableSlope2,
]: [tEthereumAddress, string, string, string, string, string],
verify: boolean
) => {
const id = eContractid.DefaultReserveInterestRateStrategy;
const args = [
addressesProvider,
baseVariableBorrowRate,
variableSlope1,
variableSlope2,
stableSlope1,
stableSlope2,
];
const instance = await deployContract<DefaultReserveInterestRateStrategy>(id, args);
if (verify) {
await verifyContract(id, instance.address, args);
}
return instance;
};
export const deployStableDebtToken = async (
2020-09-24 15:48:29 +00:00
[name, symbol, underlyingAsset, poolAddress, incentivesController]: [
string,
string,
tEthereumAddress,
2020-09-24 15:48:29 +00:00
tEthereumAddress,
tEthereumAddress
],
verify: boolean
) => {
const id = eContractid.StableDebtToken;
2020-09-24 15:48:29 +00:00
const args = [poolAddress, underlyingAsset, name, symbol, incentivesController];
const instance = await deployContract<StableDebtToken>(id, args);
2020-06-30 12:09:28 +00:00
if (verify) {
await verifyContract(id, instance.address, args);
}
return instance;
2020-07-13 08:54:08 +00:00
};
export const deployVariableDebtToken = async (
2020-09-24 15:48:29 +00:00
[name, symbol, underlyingAsset, poolAddress, incentivesController]: [
string,
string,
tEthereumAddress,
2020-09-24 15:48:29 +00:00
tEthereumAddress,
tEthereumAddress
],
verify: boolean
) => {
const id = eContractid.VariableDebtToken;
2020-09-24 15:48:29 +00:00
const args = [poolAddress, underlyingAsset, name, symbol, incentivesController];
const instance = await deployContract<VariableDebtToken>(id, args);
2020-07-03 09:15:30 +00:00
if (verify) {
await verifyContract(id, instance.address, args);
}
return instance;
2020-07-13 08:54:08 +00:00
};
export const deployGenericAToken = async (
2020-09-24 15:48:29 +00:00
[poolAddress, underlyingAssetAddress, name, symbol, incentivesController]: [
tEthereumAddress,
tEthereumAddress,
string,
2020-09-24 15:48:29 +00:00
string,
tEthereumAddress
],
verify: boolean
) => {
const id = eContractid.AToken;
2020-10-08 13:41:48 +00:00
const args = [
2020-08-10 18:20:08 +00:00
poolAddress,
underlyingAssetAddress,
2020-10-08 13:41:48 +00:00
ZERO_ADDRESS,
2020-08-10 18:20:08 +00:00
name,
symbol,
2020-09-15 14:02:21 +00:00
incentivesController,
2020-10-08 13:41:48 +00:00
];
const instance = await deployContract<AToken>(id, args);
2020-08-07 17:29:13 +00:00
if (verify) {
await verifyContract(id, instance.address, args);
}
return instance;
2020-08-07 17:29:13 +00:00
};
const linkBytecode = (artifact: Artifact, libraries: any) => {
let bytecode = artifact.bytecode;
2020-07-13 08:54:08 +00:00
for (const [fileName, fileReferences] of Object.entries(artifact.linkReferences)) {
for (const [libName, fixups] of Object.entries(fileReferences)) {
const addr = libraries[libName];
if (addr === undefined) {
continue;
}
for (const fixup of fixups) {
bytecode =
bytecode.substr(0, 2 + fixup.start * 2) +
addr.substr(2) +
bytecode.substr(2 + (fixup.start + fixup.length) * 2);
}
}
}
return bytecode;
};
export const getParamPerNetwork = <T>(
2020-09-14 13:57:11 +00:00
{kovan, ropsten, main, buidlerevm, coverage}: iParamsPerNetwork<T>,
network: eEthereumNetwork
) => {
switch (network) {
2020-09-14 13:57:11 +00:00
case eEthereumNetwork.coverage:
return coverage;
case eEthereumNetwork.buidlerevm:
return buidlerevm;
case eEthereumNetwork.kovan:
return kovan;
case eEthereumNetwork.ropsten:
return ropsten;
case eEthereumNetwork.main:
return main;
default:
return main;
}
};
2020-07-13 08:54:08 +00:00
export const getParamPerPool = <T>({proto, secondary}: iParamsPerPool<T>, pool: AavePools) => {
switch (pool) {
case AavePools.proto:
return proto;
case AavePools.secondary:
return secondary;
default:
return proto;
}
};
2020-07-13 08:54:08 +00:00
export const convertToCurrencyDecimals = async (tokenAddress: tEthereumAddress, amount: string) => {
const token = await getIErc20Detailed(tokenAddress);
let decimals = (await token.decimals()).toString();
return ethers.utils.parseUnits(amount, decimals);
};
2020-07-13 08:54:08 +00:00
export const convertToCurrencyUnits = async (tokenAddress: string, amount: string) => {
const token = await getIErc20Detailed(tokenAddress);
let decimals = new BigNumber(await token.decimals());
const currencyUnit = new BigNumber(10).pow(decimals);
const amountInCurrencyUnits = new BigNumber(amount).div(currencyUnit);
return amountInCurrencyUnits.toFixed();
};
2020-09-14 13:57:11 +00:00
2020-08-20 15:38:57 +00:00
export const deployAllMockTokens = async (verify?: boolean) => {
const tokens: {[symbol: string]: MockContract | MintableERC20} = {};
2020-08-20 15:38:57 +00:00
const protoConfigData = getReservesConfigByPool(AavePools.proto);
const secondaryConfigData = getReservesConfigByPool(AavePools.secondary);
for (const tokenSymbol of Object.keys(TokenContractId)) {
let decimals = 18;
let configData = (<any>protoConfigData)[tokenSymbol];
if (!configData) {
configData = (<any>secondaryConfigData)[tokenSymbol];
}
if (!configData) {
decimals = 18;
}
tokens[tokenSymbol] = await deployMintableERC20([
2020-08-20 15:38:57 +00:00
tokenSymbol,
tokenSymbol,
configData ? configData.reserveDecimals : 18,
]);
await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]);
if (verify) {
await verifyContract(eContractid.MintableERC20, tokens[tokenSymbol].address, []);
2020-08-20 15:38:57 +00:00
}
}
return tokens;
};
export const deployMockTokens = async (config: PoolConfiguration, verify?: boolean) => {
const tokens: {[symbol: string]: MockContract | MintableERC20} = {};
2020-08-25 12:15:35 +00:00
const defaultDecimals = 18;
const configData = config.ReservesConfig;
for (const tokenSymbol of Object.keys(config.ReserveSymbols)) {
tokens[tokenSymbol] = await deployMintableERC20([
2020-08-25 12:15:35 +00:00
tokenSymbol,
tokenSymbol,
Number(configData[tokenSymbol as keyof iMultiPoolsAssets<IReserveParams>].reserveDecimals) ||
defaultDecimals,
]);
await registerContractInJsonDb(tokenSymbol.toUpperCase(), tokens[tokenSymbol]);
if (verify) {
await verifyContract(eContractid.MintableERC20, tokens[tokenSymbol].address, []);
2020-08-25 12:15:35 +00:00
}
}
return tokens;
};
2020-09-14 13:57:11 +00:00
export const buildPermitParams = (
chainId: number,
token: tEthereumAddress,
revision: string,
tokenName: string,
owner: tEthereumAddress,
spender: tEthereumAddress,
nonce: number,
deadline: string,
value: tStringTokenSmallUnits
) => ({
types: {
EIP712Domain: [
2020-09-15 14:02:21 +00:00
{name: 'name', type: 'string'},
{name: 'version', type: 'string'},
{name: 'chainId', type: 'uint256'},
{name: 'verifyingContract', type: 'address'},
2020-09-14 13:57:11 +00:00
],
Permit: [
2020-09-15 14:02:21 +00:00
{name: 'owner', type: 'address'},
{name: 'spender', type: 'address'},
{name: 'value', type: 'uint256'},
{name: 'nonce', type: 'uint256'},
{name: 'deadline', type: 'uint256'},
2020-09-14 13:57:11 +00:00
],
},
2020-09-15 14:02:21 +00:00
primaryType: 'Permit' as const,
2020-09-14 13:57:11 +00:00
domain: {
name: tokenName,
version: revision,
chainId: chainId,
verifyingContract: token,
},
message: {
owner,
spender,
value,
nonce,
deadline,
},
});
export const getSignatureFromTypedData = (
privateKey: string,
typedData: any // TODO: should be TypedData, from eth-sig-utils, but TS doesn't accept it
): ECDSASignature => {
2020-09-15 14:02:21 +00:00
const signature = signTypedData_v4(Buffer.from(privateKey.substring(2, 66), 'hex'), {
data: typedData,
});
2020-09-14 13:57:11 +00:00
return fromRpcSig(signature);
2020-09-15 14:02:21 +00:00
};