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

410 lines
12 KiB
TypeScript
Raw Permalink Normal View History

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,
iEthereumParamsPerNetwork,
iPolygonParamsPerNetwork,
iXDaiParamsPerNetwork,
iAvalancheParamsPerNetwork,
eAvalancheNetwork,
2020-07-13 08:54:08 +00:00
} 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 { getDefenderRelaySigner, usingDefender } from './defender-utils';
2020-08-25 12:15:35 +00:00
export type MockTokenMap = { [symbol: string]: MintableERC20 };
2020-07-13 08:54:08 +00:00
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'))) {
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}.${DRE.network.name}`, {
address,
})
.write();
2020-11-10 13:18:48 +00:00
export const rawInsertContractAddressInDb = async (id: string, address: tEthereumAddress) =>
await getDb()
.set(`${id}.${DRE.network.name}`, {
address,
})
.write();
export const getEthersSigners = async (): Promise<Signer[]> => {
const ethersSigners = await Promise.all(await DRE.ethers.getSigners());
if (usingDefender()) {
const [, ...users] = ethersSigners;
return [await getDefenderRelaySigner(), ...users];
}
return ethersSigners;
};
2020-05-29 14:55:31 +00:00
2020-07-13 08:54:08 +00:00
export const getEthersSignersAddresses = async (): Promise<tEthereumAddress[]> =>
await Promise.all((await getEthersSigners()).map((signer) => signer.getAddress()));
2020-05-29 14:55:31 +00:00
export const getCurrentBlock = async () => {
return DRE.ethers.provider.getBlockNumber();
2020-05-29 14:55:31 +00:00
};
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> => {
const contract = (await (await DRE.ethers.getContractFactory(contractName))
.connect(await getFirstSigner())
.deploy(...args)) as ContractType;
2020-10-21 09:42:27 +00:00
await waitForTx(contract.deployTransaction);
await registerContractInJsonDb(<eContractid>contractName, contract);
return contract;
};
export const withSaveAndVerify = async <ContractType extends Contract>(
instance: ContractType,
2020-10-15 17:19:02 +00:00
id: string,
args: (string | string[])[],
verify?: boolean
): Promise<ContractType> => {
2020-10-23 13:18:01 +00:00
await waitForTx(instance.deployTransaction);
2020-10-15 17:19:02 +00:00
await registerContractInJsonDb(id, instance);
if (verify) {
await verifyContract(id, instance, args);
2020-10-15 17:19:02 +00:00
}
return instance;
};
export const getContract = async <ContractType extends Contract>(
2020-05-29 14:55:31 +00:00
contractName: string,
address: string
): Promise<ContractType> => (await DRE.ethers.getContractAt(contractName, address)) as ContractType;
2020-05-29 14:55:31 +00:00
export const linkBytecode = (artifact: BuidlerArtifact | 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>(param: iParamsPerNetwork<T>, network: eNetwork) => {
const { main, ropsten, kovan, coverage, buidlerevm, tenderly } =
param as iEthereumParamsPerNetwork<T>;
const { matic, mumbai } = param as iPolygonParamsPerNetwork<T>;
const { xdai } = param as iXDaiParamsPerNetwork<T>;
const { avalanche, fuji } = param as iAvalancheParamsPerNetwork<T>;
if (process.env.FORK) {
return param[process.env.FORK as eNetwork] as T;
}
switch (network) {
2020-09-14 13:57:11 +00:00
case eEthereumNetwork.coverage:
return coverage;
case eEthereumNetwork.buidlerevm:
return buidlerevm;
2020-11-05 14:31:35 +00:00
case eEthereumNetwork.hardhat:
return buidlerevm;
case eEthereumNetwork.kovan:
return kovan;
case eEthereumNetwork.ropsten:
return ropsten;
case eEthereumNetwork.main:
2021-02-24 21:44:45 +00:00
return main;
case eEthereumNetwork.tenderly:
return tenderly;
case ePolygonNetwork.matic:
return matic;
case ePolygonNetwork.mumbai:
return mumbai;
case eXDaiNetwork.xdai:
return xdai;
case eAvalancheNetwork.avalanche:
return avalanche;
case eAvalancheNetwork.fuji:
return fuji;
}
};
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, avalanche }: iParamsPerPool<T>,
pool: AavePools
) => {
switch (pool) {
case AavePools.proto:
return proto;
2021-02-24 21:42:41 +00:00
case AavePools.amm:
return amm;
2021-02-24 21:49:23 +00:00
case AavePools.matic:
return matic;
case AavePools.avalanche:
return avalanche;
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
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' },
2020-09-14 13:57:11 +00:00
],
Permit: [
{ 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
};
2020-11-06 15:12:40 +00:00
export const buildLiquiditySwapParams = (
assetToSwapToList: tEthereumAddress[],
minAmountsToReceive: BigNumberish[],
swapAllBalances: BigNumberish[],
permitAmounts: BigNumberish[],
deadlines: BigNumberish[],
v: BigNumberish[],
r: (string | Buffer)[],
s: (string | Buffer)[],
useEthPath: boolean[]
2020-11-06 15:12:40 +00:00
) => {
return ethers.utils.defaultAbiCoder.encode(
[
'address[]',
'uint256[]',
'bool[]',
'uint256[]',
'uint256[]',
'uint8[]',
'bytes32[]',
'bytes32[]',
'bool[]',
2020-11-06 15:12:40 +00:00
],
[
assetToSwapToList,
minAmountsToReceive,
swapAllBalances,
permitAmounts,
deadlines,
v,
r,
s,
useEthPath,
]
2020-11-06 15:12:40 +00:00
);
};
export const buildRepayAdapterParams = (
collateralAsset: tEthereumAddress,
collateralAmount: BigNumberish,
rateMode: BigNumberish,
permitAmount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: string | Buffer,
s: string | Buffer,
useEthPath: boolean
2020-11-06 15:12:40 +00:00
) => {
return ethers.utils.defaultAbiCoder.encode(
['address', 'uint256', 'uint256', 'uint256', 'uint256', 'uint8', 'bytes32', 'bytes32', 'bool'],
[collateralAsset, collateralAmount, rateMode, permitAmount, deadline, v, r, s, useEthPath]
2020-11-06 15:12:40 +00:00
);
};
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 buildParaSwapLiquiditySwapParams = (
assetToSwapTo: tEthereumAddress,
minAmountToReceive: BigNumberish,
swapAllBalanceOffset: BigNumberish,
swapCalldata: string | Buffer,
augustus: tEthereumAddress,
permitAmount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: string | Buffer,
s: string | Buffer
) => {
return ethers.utils.defaultAbiCoder.encode(
[
'address',
'uint256',
'uint256',
'bytes',
'address',
'tuple(uint256,uint256,uint8,bytes32,bytes32)',
],
[
assetToSwapTo,
minAmountToReceive,
swapAllBalanceOffset,
swapCalldata,
augustus,
[permitAmount, deadline, v, r, s],
]
);
};
2021-06-15 15:52:42 +00:00
export const verifyContract = async (
id: string,
instance: Contract,
args: (string | string[])[]
) => {
2021-12-17 15:23:11 +00:00
if (usingTenderly()) {
await verifyAtTenderly(id, instance);
}
2021-12-17 15:23:11 +00:00
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`);
};