mirror of
				https://github.com/Instadapp/aave-protocol-v2.git
				synced 2024-07-29 21:47:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { Contract, Signer, utils, ethers, BigNumberish } from 'ethers';
 | |
| import { signTypedData_v4 } from 'eth-sig-util';
 | |
| import { fromRpcSig, ECDSASignature } from 'ethereumjs-util';
 | |
| import BigNumber from 'bignumber.js';
 | |
| import { getDb, DRE, waitForTx, notFalsyOrZeroAddress } from './misc-utils';
 | |
| import {
 | |
|   tEthereumAddress,
 | |
|   eContractid,
 | |
|   tStringTokenSmallUnits,
 | |
|   eEthereumNetwork,
 | |
|   AavePools,
 | |
|   iParamsPerNetwork,
 | |
|   iParamsPerPool,
 | |
|   ePolygonNetwork,
 | |
|   eXDaiNetwork,
 | |
|   eNetwork,
 | |
|   iParamsPerNetworkAll,
 | |
|   iEthereumParamsPerNetwork,
 | |
|   iPolygonParamsPerNetwork,
 | |
|   iXDaiParamsPerNetwork,
 | |
| } from './types';
 | |
| import { MintableERC20 } from '../types/MintableERC20';
 | |
| import { Artifact } from 'hardhat/types';
 | |
| import { Artifact as BuidlerArtifact } from '@nomiclabs/buidler/types';
 | |
| import { verifyEtherscanContract } from './etherscan-verification';
 | |
| import { getFirstSigner, getIErc20Detailed } from './contracts-getters';
 | |
| import { usingTenderly, verifyAtTenderly } from './tenderly-utils';
 | |
| import { usingPolygon, verifyAtPolygon } from './polygon-utils';
 | |
| import { ConfigNames, loadPoolConfig } from './configuration';
 | |
| import { ZERO_ADDRESS } from './constants';
 | |
| import { RewardsTokenFactory, RewardsATokenMockFactory } from '../types';
 | |
| 
 | |
| export type MockTokenMap = { [symbol: string]: MintableERC20 };
 | |
| 
 | |
| export const registerContractInJsonDb = async (contractId: string, contractInstance: Contract) => {
 | |
|   const currentNetwork = DRE.network.name;
 | |
|   const FORK = process.env.FORK;
 | |
|   if (FORK || (currentNetwork !== 'hardhat' && !currentNetwork.includes('coverage'))) {
 | |
|     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();
 | |
| };
 | |
| 
 | |
| export const insertContractAddressInDb = async (id: eContractid, address: tEthereumAddress) =>
 | |
|   await getDb()
 | |
|     .set(`${id}.${DRE.network.name}`, {
 | |
|       address,
 | |
|     })
 | |
|     .write();
 | |
| 
 | |
| export const rawInsertContractAddressInDb = async (id: string, address: tEthereumAddress) =>
 | |
|   await getDb()
 | |
|     .set(`${id}.${DRE.network.name}`, {
 | |
|       address,
 | |
|     })
 | |
|     .write();
 | |
| 
 | |
| export const getEthersSigners = async (): Promise<Signer[]> =>
 | |
|   await Promise.all(await DRE.ethers.getSigners());
 | |
| 
 | |
| export const getEthersSignersAddresses = async (): Promise<tEthereumAddress[]> =>
 | |
|   await Promise.all((await DRE.ethers.getSigners()).map((signer) => signer.getAddress()));
 | |
| 
 | |
| export const getCurrentBlock = async () => {
 | |
|   return DRE.ethers.provider.getBlockNumber();
 | |
| };
 | |
| 
 | |
| export const decodeAbiNumber = (data: string): number =>
 | |
|   parseInt(utils.defaultAbiCoder.decode(['uint256'], data).toString());
 | |
| 
 | |
| export const deployContract = async <ContractType extends Contract>(
 | |
|   contractName: string,
 | |
|   args: any[]
 | |
| ): Promise<ContractType> => {
 | |
|   const contract = (await (await DRE.ethers.getContractFactory(contractName)).deploy(
 | |
|     ...args
 | |
|   )) as ContractType;
 | |
|   await waitForTx(contract.deployTransaction);
 | |
|   await registerContractInJsonDb(<eContractid>contractName, contract);
 | |
|   return contract;
 | |
| };
 | |
| 
 | |
| export const withSaveAndVerify = async <ContractType extends Contract>(
 | |
|   instance: ContractType,
 | |
|   id: string,
 | |
|   args: (string | string[])[],
 | |
|   verify?: boolean
 | |
| ): Promise<ContractType> => {
 | |
|   await waitForTx(instance.deployTransaction);
 | |
|   await registerContractInJsonDb(id, instance);
 | |
|   if (verify) {
 | |
|     await verifyContract(id, instance, args);
 | |
|   }
 | |
|   return instance;
 | |
| };
 | |
| 
 | |
| export const getContract = async <ContractType extends Contract>(
 | |
|   contractName: string,
 | |
|   address: string
 | |
| ): Promise<ContractType> => (await DRE.ethers.getContractAt(contractName, address)) as ContractType;
 | |
| 
 | |
| export const linkBytecode = (artifact: BuidlerArtifact | Artifact, libraries: any) => {
 | |
|   let bytecode = artifact.bytecode;
 | |
| 
 | |
|   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>(param: iParamsPerNetwork<T>, network: eNetwork) => {
 | |
|   const {
 | |
|     main,
 | |
|     ropsten,
 | |
|     kovan,
 | |
|     coverage,
 | |
|     buidlerevm,
 | |
|     tenderlyMain,
 | |
|   } = param as iEthereumParamsPerNetwork<T>;
 | |
|   const { matic, mumbai } = param as iPolygonParamsPerNetwork<T>;
 | |
|   const { xdai } = param as iXDaiParamsPerNetwork<T>;
 | |
|   if (process.env.FORK) {
 | |
|     return param[process.env.FORK as eNetwork] as T;
 | |
|   }
 | |
| 
 | |
|   switch (network) {
 | |
|     case eEthereumNetwork.coverage:
 | |
|       return coverage;
 | |
|     case eEthereumNetwork.buidlerevm:
 | |
|       return buidlerevm;
 | |
|     case eEthereumNetwork.hardhat:
 | |
|       return buidlerevm;
 | |
|     case eEthereumNetwork.kovan:
 | |
|       return kovan;
 | |
|     case eEthereumNetwork.ropsten:
 | |
|       return ropsten;
 | |
|     case eEthereumNetwork.main:
 | |
|       return main;
 | |
|     case eEthereumNetwork.tenderlyMain:
 | |
|       return tenderlyMain;
 | |
|     case ePolygonNetwork.matic:
 | |
|       return matic;
 | |
|     case ePolygonNetwork.mumbai:
 | |
|       return mumbai;
 | |
|     case eXDaiNetwork.xdai:
 | |
|       return xdai;
 | |
|   }
 | |
| };
 | |
| 
 | |
| export const getOptionalParamAddressPerNetwork = (
 | |
|   param: iParamsPerNetwork<tEthereumAddress> | undefined | null,
 | |
|   network: eNetwork
 | |
| ) => {
 | |
|   if (!param) {
 | |
|     return ZERO_ADDRESS;
 | |
|   }
 | |
|   return getParamPerNetwork(param, network);
 | |
| };
 | |
| 
 | |
| export const getParamPerPool = <T>({ proto, amm, matic }: iParamsPerPool<T>, pool: AavePools) => {
 | |
|   switch (pool) {
 | |
|     case AavePools.proto:
 | |
|       return proto;
 | |
|     case AavePools.amm:
 | |
|       return amm;
 | |
|     case AavePools.matic:
 | |
|       return matic;
 | |
|     default:
 | |
|       return proto;
 | |
|   }
 | |
| };
 | |
| 
 | |
| 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);
 | |
| };
 | |
| 
 | |
| 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();
 | |
| };
 | |
| 
 | |
| export const buildPermitParams = (
 | |
|   chainId: number,
 | |
|   token: tEthereumAddress,
 | |
|   revision: string,
 | |
|   tokenName: string,
 | |
|   owner: tEthereumAddress,
 | |
|   spender: tEthereumAddress,
 | |
|   nonce: number,
 | |
|   deadline: string,
 | |
|   value: tStringTokenSmallUnits
 | |
| ) => ({
 | |
|   types: {
 | |
|     EIP712Domain: [
 | |
|       { name: 'name', type: 'string' },
 | |
|       { name: 'version', type: 'string' },
 | |
|       { name: 'chainId', type: 'uint256' },
 | |
|       { name: 'verifyingContract', type: 'address' },
 | |
|     ],
 | |
|     Permit: [
 | |
|       { name: 'owner', type: 'address' },
 | |
|       { name: 'spender', type: 'address' },
 | |
|       { name: 'value', type: 'uint256' },
 | |
|       { name: 'nonce', type: 'uint256' },
 | |
|       { name: 'deadline', type: 'uint256' },
 | |
|     ],
 | |
|   },
 | |
|   primaryType: 'Permit' as const,
 | |
|   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 => {
 | |
|   const signature = signTypedData_v4(Buffer.from(privateKey.substring(2, 66), 'hex'), {
 | |
|     data: typedData,
 | |
|   });
 | |
|   return fromRpcSig(signature);
 | |
| };
 | |
| 
 | |
| export const buildLiquiditySwapParams = (
 | |
|   assetToSwapToList: tEthereumAddress[],
 | |
|   minAmountsToReceive: BigNumberish[],
 | |
|   swapAllBalances: BigNumberish[],
 | |
|   permitAmounts: BigNumberish[],
 | |
|   deadlines: BigNumberish[],
 | |
|   v: BigNumberish[],
 | |
|   r: (string | Buffer)[],
 | |
|   s: (string | Buffer)[],
 | |
|   useEthPath: boolean[]
 | |
| ) => {
 | |
|   return ethers.utils.defaultAbiCoder.encode(
 | |
|     [
 | |
|       'address[]',
 | |
|       'uint256[]',
 | |
|       'bool[]',
 | |
|       'uint256[]',
 | |
|       'uint256[]',
 | |
|       'uint8[]',
 | |
|       'bytes32[]',
 | |
|       'bytes32[]',
 | |
|       'bool[]',
 | |
|     ],
 | |
|     [
 | |
|       assetToSwapToList,
 | |
|       minAmountsToReceive,
 | |
|       swapAllBalances,
 | |
|       permitAmounts,
 | |
|       deadlines,
 | |
|       v,
 | |
|       r,
 | |
|       s,
 | |
|       useEthPath,
 | |
|     ]
 | |
|   );
 | |
| };
 | |
| 
 | |
| export const buildRepayAdapterParams = (
 | |
|   collateralAsset: tEthereumAddress,
 | |
|   collateralAmount: BigNumberish,
 | |
|   rateMode: BigNumberish,
 | |
|   permitAmount: BigNumberish,
 | |
|   deadline: BigNumberish,
 | |
|   v: BigNumberish,
 | |
|   r: string | Buffer,
 | |
|   s: string | Buffer,
 | |
|   useEthPath: boolean
 | |
| ) => {
 | |
|   return ethers.utils.defaultAbiCoder.encode(
 | |
|     ['address', 'uint256', 'uint256', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32', 'bool'],
 | |
|     [collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s, useEthPath]
 | |
|   );
 | |
| };
 | |
| 
 | |
| export const buildFlashLiquidationAdapterParams = (
 | |
|   collateralAsset: tEthereumAddress,
 | |
|   debtAsset: tEthereumAddress,
 | |
|   user: tEthereumAddress,
 | |
|   debtToCover: BigNumberish,
 | |
|   useEthPath: boolean
 | |
| ) => {
 | |
|   return ethers.utils.defaultAbiCoder.encode(
 | |
|     ['address', 'address', 'address', 'uint256', 'bool'],
 | |
|     [collateralAsset, debtAsset, user, debtToCover, useEthPath]
 | |
|   );
 | |
| };
 | |
| 
 | |
| export const verifyContract = async (
 | |
|   id: string,
 | |
|   instance: Contract,
 | |
|   args: (string | string[])[]
 | |
| ) => {
 | |
|   if (usingPolygon()) {
 | |
|     await verifyAtPolygon(id, instance, args);
 | |
|   } else {
 | |
|     if (usingTenderly()) {
 | |
|       await verifyAtTenderly(id, instance);
 | |
|     }
 | |
|     await verifyEtherscanContract(instance.address, args);
 | |
|   }
 | |
|   return instance;
 | |
| };
 | |
| 
 | |
| export const getContractAddressWithJsonFallback = async (
 | |
|   id: string,
 | |
|   pool: ConfigNames
 | |
| ): Promise<tEthereumAddress> => {
 | |
|   const poolConfig = loadPoolConfig(pool);
 | |
|   const network = <eNetwork>DRE.network.name;
 | |
|   const db = getDb();
 | |
| 
 | |
|   const contractAtMarketConfig = getOptionalParamAddressPerNetwork(poolConfig[id], network);
 | |
|   if (notFalsyOrZeroAddress(contractAtMarketConfig)) {
 | |
|     return contractAtMarketConfig;
 | |
|   }
 | |
| 
 | |
|   const contractAtDb = await getDb().get(`${id}.${DRE.network.name}`).value();
 | |
|   if (contractAtDb?.address) {
 | |
|     return contractAtDb.address as tEthereumAddress;
 | |
|   }
 | |
|   throw Error(`Missing contract address ${id} at Market config and JSON local db`);
 | |
| };
 | 
